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
