Come to think of it, there's a problem. PS's LOOP uses BIND. It's super handy and I rely on it all the time to do things like (loop :for (a (:b)) :in c...). I suppose there are a few options for dealing with this but the easiest would be to keep (or move) LOOP into the same package as BIND.
On Thu, Jan 17, 2013 at 3:13 PM, Daniel Gackle <[email protected]>wrote: > I think it makes sense to put BIND in a more outer circle, so to speak. > It's definitely more of a library macro, not any kind of standard. > > I'm not sure I understand this, though: > > > The non-CL stuff should also, whenever possible, define > > macros/functions with equivalent functionality in Common Lisp, to > > provide an easy way to run PS code in CL. > > Do you mean that there should be a CL implementation > of (to use the same example) BIND? > > Dan > > > > On Thu, Jan 17, 2013 at 12:41 PM, Vladimir Sedach <[email protected]>wrote: > >> I'm all for documenting LOOP extensions. I was actually thinking of >> deprecating BIND and BIND* since they're non-cl. I propose this: split >> off CL and non-CL stuff into two different packages, that are both >> imported and re-exported from the PARENSCRIPT package. The non-CL >> stuff should also, whenever possible, define macros/functions with >> equivalent functionality in Common Lisp, to provide an easy way to run >> PS code in CL. >> >> Thoughts? >> >> Vladimir >> >> On Tue, Jan 15, 2013 at 12:30 PM, Daniel Gackle <[email protected]> >> wrote: >> > Vladimir -- writing the below made me think of whether we should >> > incorporate it into the PS docs somehow, with whatever changes >> > are needed to fit the docs' conventions. Do you agree? What would >> > be the best way to do this? >> > >> > We really should document LOOP as well, especially since it >> > has the :MAP and :OF extensions for working with JS objects. >> > >> > On Tue, Jan 15, 2013 at 1:20 PM, Daniel Gackle <[email protected]> >> > wrote: >> >> >> >> Hi David, >> >> >> >> Parenscript does have such an operator, called BIND. Since it hasn't >> >> been documented yet, here are some examples. The idea is that you use >> >> keywords to bind to object properties and ordinary symbols to bind to >> >> array elements. Let's look at arrays first. A simple example: >> >> >> >> (bind (a b c) '(10 20 30) >> >> (list c b a)) >> >> => (30 20 10) >> >> >> >> Bind elements to NIL to ignore them: >> >> >> >> (bind (a nil c) '(10 20 30) >> >> (list c a)) >> >> => (30 10) >> >> >> >> To ignore the tail of an array, just omit it: >> >> >> >> (bind (a b) '(10 20 30 40) >> >> (list b a)) >> >> => (20 10) >> >> >> >> You can use &rest (or .) in the destructuring list: >> >> >> >> (bind (a &rest others) '(10 20 30) >> >> (list others a)) >> >> => ((20 30) 10) >> >> >> >> (bind (a . others) '(10 20 30) >> >> (list others a)) >> >> => same >> >> >> >> You can nest array bindings: >> >> >> >> (bind (a (b (c d))) >> >> '(10 (20 (30 40))) >> >> (list d c b a)) >> >> => (40 30 20 10) >> >> >> >> Now for objects. A simple example: >> >> >> >> (bind (:a :b :c) (create :a 10 :b 20 :c 30) >> >> (list c b a)) >> >> => (30 20 10) >> >> >> >> Since the properties are named, order doesn't matter: >> >> >> >> (bind (:a :c :b) (create :a 10 :b 20 :c 30) >> >> (list c b a)) >> >> => (30 20 10) >> >> >> >> If you want to bind to a property using a different name, you can use >> >> a binding pair instead of a keyword: >> >> >> >> (bind ((my-name :original)) (create :original 10) >> >> (list my-name)) >> >> => (10) >> >> >> >> I use that sparingly because the extra parens can impede >> >> readability, but it's handy to avoid naming collisions: >> >> >> >> (let ((original 99)) >> >> (bind ((mine :original)) (create :original 10) >> >> (list original mine))) >> >> => (99 10) >> >> >> >> You can bind to an object inside an array: >> >> >> >> (bind (a (:b)) (list 10 (create :b 20)) >> >> (list b a)) >> >> => (20 10) >> >> >> >> However, you can't bind to an array inside an object, or an object >> >> inside an object, in a single BIND form — you have to use two: >> >> >> >> (bind (:a) (make :a '(10 20 30)) >> >> (bind (nil b c) a >> >> (list c b))) >> >> => (30 20) >> >> >> >> (bind (:a) (make :a (make :b 20 :c 30)) >> >> (bind (:b :c) a >> >> (list c b))) >> >> => (30 20) >> >> >> >> That's because the notation doesn't seem to allow for any unambiguous >> >> way to do such nesting. (If you can think of one, please show us some >> >> examples.) This is the chief difference from the notation in your >> >> example, which adds additional syntax to support more complex >> >> destructuring lists. The tradeoff here is that BIND, lacking syntax, >> >> handles the simplest and most common cases more elegantly. >> >> >> >> There is a form BIND* which allows multiple binds in a row to avoid >> >> unwanted indentation: >> >> >> >> (bind* ((a b) '(10 20) >> >> (:c) (make :c 30)) >> >> (list a b c)) >> >> => (10 20 30) >> >> >> >> It simply takes a list of binding pairs and turns them into a nested >> >> series of BIND forms. >> >> >> >> Finally, note that if you mix keyword and non-keyword symbols in a >> >> binding list, it's considered an array binding and not an object >> >> binding: >> >> >> >> (bind (:a b) '(10 20) >> >> (list b a)) >> >> => (20 10) >> >> >> >> (bind (:a b) (make :a 10 :b 20) >> >> (list b a)) >> >> => (undefined undefined) >> >> >> >> But it's bad practice to mix keywords and non-keywords in >> >> the same binding list. Perhaps BIND should throw an error >> >> when given such input. >> >> >> >> So now let's look at your example: >> >> >> >> (d-bind (:obj name (:obj firstname lastname) >> >> likes (:arr first-like second-like)) >> >> (create :name (create :firstname "Joe" :lastname "Blo") >> >> :occupation "Web Developer" >> >> :likes '("programming" "woodworking" "cycling")) >> >> (alert (+ "Your name is " firstname " and you like " >> >> first-like))) >> >> >> >> As I mentioned, the main difference is that PS's BIND doesn't use >> >> special syntax like :obj and :arr to convey what's being destructured. >> >> No doubt tastes will differ on this. In any case, to translate your >> >> example, we'll have to use nested BINDs: >> >> >> >> (bind (:name :likes) >> >> (create :name (create :firstname "Joe" :lastname "Blo") >> >> :occupation "Web Developer" >> >> :likes '("programming" "woodworking" "cycling")) >> >> (bind (:firstname) name >> >> (bind (first-like) likes >> >> (+ "Your name is " firstname " and you like " first-like)))) >> >> => "Your name is Joe and you like programming" >> >> >> >> We can do the same thing with BIND* like this: >> >> >> >> (bind* ((:name :likes) (create :name (create :firstname "Joe" >> :lastname >> >> "Blo") >> >> :occupation "Web Developer" >> >> :likes '("programming" "woodworking" >> >> "cycling")) >> >> (:firstname) name >> >> (first-like) likes) >> >> (+ "Your name is " firstname " and you like " first-like)) >> >> => "Your name is Joe and you like programming" >> >> >> >> It would be a straightforward exercise to write your D-BIND as a macro >> >> that interprets the :obj and :arr directives, uses gensyms to create >> >> intermediate bindings, and emits the above nested BIND form. >> >> >> >> If you find anything else in CoffeeScript that you think would be a >> >> natural fit for PS, please post it here. >> >> >> >> Daniel >> >> >> > >> > >> > _______________________________________________ >> > parenscript-devel mailing list >> > [email protected] >> > http://lists.common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel >> > >> >> _______________________________________________ >> parenscript-devel mailing list >> [email protected] >> http://lists.common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel >> > >
_______________________________________________ parenscript-devel mailing list [email protected] http://lists.common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel
