Vladimir -- writing the below made me think of whether we should<div>incorporate it into the PS docs somehow, with whatever changes</div><div>are needed to fit the docs' conventions. Do you agree? What would</div><div>
be the best way to do this?</div><div><br></div><div>We really should document LOOP as well, especially since it</div><div>has the :MAP and :OF extensions for working with JS objects.</div><div><br><div class="gmail_quote">
On Tue, Jan 15, 2013 at 1:20 PM, Daniel Gackle <span dir="ltr"><<a href="mailto:danielgackle@gmail.com" target="_blank">danielgackle@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div><div>Hi David,</div><div><br></div><div>Parenscript does have such an operator, called BIND. Since it hasn't</div><div>been documented yet, here are some examples. The idea is that you use</div><div>keywords to bind to object properties and ordinary symbols to bind to</div>
<div>array elements. Let's look at arrays first. A simple example:</div><div><br></div><div> (bind (a b c) '(10 20 30)</div><div> (list c b a))</div><div> => (30 20 10)</div><div><br></div><div>Bind elements to NIL to ignore them:</div>
<div><br></div><div> (bind (a nil c) '(10 20 30)</div><div> (list c a))</div><div> => (30 10)</div><div><br></div><div>To ignore the tail of an array, just omit it:</div><div><br></div><div> (bind (a b) '(10 20 30 40)</div>
<div> (list b a))</div><div> => (20 10)</div><div><br></div><div>You can use &rest (or .) in the destructuring list:</div><div><br></div><div> (bind (a &rest others) '(10 20 30)</div><div> (list others a))</div>
<div> => ((20 30) 10)</div><div><br></div><div> (bind (a . others) '(10 20 30)</div><div> (list others a))</div><div> => same</div><div><br></div><div>You can nest array bindings:</div><div><br></div><div>
(bind (a (b (c d)))</div><div> '(10 (20 (30 40)))</div><div> (list d c b a))</div><div> => (40 30 20 10)</div><div><br></div><div>Now for objects. A simple example:</div><div><br></div><div> (bind (:a :b :c) (create :a 10 :b 20 :c 30)</div>
<div> (list c b a))</div><div> => (30 20 10)</div><div><br></div><div>Since the properties are named, order doesn't matter:</div><div><br></div><div> (bind (:a :c :b) (create :a 10 :b 20 :c 30)</div><div> (list c b a))</div>
<div> => (30 20 10)</div><div><br></div><div>If you want to bind to a property using a different name, you can use</div><div>a binding pair instead of a keyword:</div><div><br></div><div> (bind ((my-name :original)) (create :original 10)</div>
<div> (list my-name))</div><div> => (10)</div><div><br></div><div>I use that sparingly because the extra parens can impede</div><div>readability, but it's handy to avoid naming collisions:</div><div><br></div>
<div> (let ((original 99))</div><div> (bind ((mine :original)) (create :original 10)</div><div> (list original mine)))</div><div> => (99 10)</div><div><br></div><div>You can bind to an object inside an array:</div>
<div><br></div><div> (bind (a (:b)) (list 10 (create :b 20))</div><div> (list b a))</div><div> => (20 10)</div><div><br></div><div>However, you can't bind to an array inside an object, or an object</div><div>
inside an object, in a single BIND form — you have to use two:</div><div><br></div><div> (bind (:a) (make :a '(10 20 30))</div><div> (bind (nil b c) a</div><div> (list c b)))</div><div> => (30 20)</div><div>
<br></div><div> (bind (:a) (make :a (make :b 20 :c 30))</div><div> (bind (:b :c) a</div><div> (list c b)))</div><div> => (30 20)</div><div><br></div><div>That's because the notation doesn't seem to allow for any unambiguous</div>
<div>way to do such nesting. (If you can think of one, please show us some</div><div>examples.) This is the chief difference from the notation in your</div><div>example, which adds additional syntax to support more complex</div>
<div>destructuring lists. The tradeoff here is that BIND, lacking syntax,</div><div>handles the simplest and most common cases more elegantly.</div><div><br></div><div>There is a form BIND* which allows multiple binds in a row to avoid</div>
<div>unwanted indentation:</div><div><br></div><div> (bind* ((a b) '(10 20)</div><div> (:c) (make :c 30))</div><div> (list a b c))</div><div> => (10 20 30)</div><div><br></div><div>It simply takes a list of binding pairs and turns them into a nested</div>
<div>series of BIND forms.</div><div><br></div><div>Finally, note that if you mix keyword and non-keyword symbols in a</div><div>binding list, it's considered an array binding and not an object</div><div>binding:</div>
<div><br></div><div> (bind (:a b) '(10 20)</div><div> (list b a))</div><div> => (20 10)</div><div><br></div><div> (bind (:a b) (make :a 10 :b 20)</div><div> (list b a))</div><div> => (undefined undefined)</div>
<div><br></div><div>But it's bad practice to mix keywords and non-keywords in</div><div>the same binding list. Perhaps BIND should throw an error</div><div>when given such input.</div><div><br></div><div>So now let's look at your example:</div>
<div class="im">
<div><br></div><div> (d-bind (:obj name (:obj firstname lastname)</div><div> likes (:arr first-like second-like))</div><div> (create :name (create :firstname "Joe" :lastname "Blo")</div>
<div> :occupation "Web Developer"</div><div> :likes '("programming" "woodworking" "cycling"))</div><div> (alert (+ "Your name is " firstname " and you like " first-like))) </div>
<div><br></div></div><div>As I mentioned, the main difference is that PS's BIND doesn't use</div><div>special syntax like :obj and :arr to convey what's being destructured.</div><div>No doubt tastes will differ on this. In any case, to translate your</div>
<div>example, we'll have to use nested BINDs:</div><div><br></div><div> (bind (:name :likes)</div><div class="im"><div> (create :name (create :firstname "Joe" :lastname "Blo")</div><div> :occupation "Web Developer"</div>
<div> :likes '("programming" "woodworking" "cycling"))</div></div><div> (bind (:firstname) name</div><div> (bind (first-like) likes</div><div> (+ "Your name is " firstname " and you like " first-like))))</div>
<div> => "Your name is Joe and you like programming"</div><div><br></div><div>We can do the same thing with BIND* like this:</div><div><br></div><div> (bind* ((:name :likes) (create :name (create :firstname "Joe" :lastname "Blo")</div>
<div class="im">
<div> :occupation "Web Developer"</div><div> :likes '("programming" "woodworking" "cycling"))</div></div><div> (:firstname) name</div>
<div> (first-like) likes)</div><div class="im"><div> (+ "Your name is " firstname " and you like " first-like))</div></div><div> => "Your name is Joe and you like programming"</div>
<div><br></div><div>
It would be a straightforward exercise to write your D-BIND as a macro</div><div>that interprets the :obj and :arr directives, uses gensyms to create</div><div>intermediate bindings, and emits the above nested BIND form.</div>
<div><br></div><div>If you find anything else in CoffeeScript that you think would be a</div><div>natural fit for PS, please post it here.</div><span class="HOEnZb"><font color="#888888"><div><br></div><div>Daniel</div></font></span></div>
<div><br></div>
</blockquote></div><br></div>