[parenscript-devel] A more useful destructuring bind

Daniel Gackle danielgackle at gmail.com
Thu Jan 17 22:13:36 UTC 2013


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 <vsedach at gmail.com> 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 <danielgackle at gmail.com>
> 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 <danielgackle at gmail.com>
> > 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
> > parenscript-devel at common-lisp.net
> > http://lists.common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel
> >
>
> _______________________________________________
> parenscript-devel mailing list
> parenscript-devel at common-lisp.net
> http://lists.common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mailman.common-lisp.net/pipermail/parenscript-devel/attachments/20130117/663b889d/attachment.html>


More information about the parenscript-devel mailing list