[parenscript-devel] PS LOOP now supports key-value iteration and collection

Vladimir Sedach vsedach at gmail.com
Tue May 8 18:23:39 UTC 2012


I really like these extensions. I read somewhere (maybe CLtL?) that
LOOP was supposed to have extension facilities. The original MIT LOOP
macro (http://code.google.com/p/mcl/source/browse/library/mit-loop.lisp?r=0c886fce5ddfb32ccf5599cdf6b632d39fa4c68d)
which is still used in a lot of implementations has facilities for
extension, and CLSQL comes with code that extends the loop macro in
several CL implementations
(http://items.sjbach.com/loop-extension.lisp).

All this to say that I think these changes would be great in Common
Lisp itself (even the MAP...TO, anything to make hash tables easier to
use in CL). You should post this to the lisp-pro mailing list
(http://lists.common-lisp.net/cgi-bin/mailman/listinfo/pro) as a
proposed CDR extension.

Vladimir

On Wed, Apr 11, 2012 at 8:05 PM, Daniel Gackle <danielgackle at gmail.com> wrote:
> I've made the following changes to PS LOOP and pushed them on a branch
> called "loop" pending the fixing of the pretty-printer as we discussed
> a couple weeks ago.
>
> These changes capture some idioms that were common in our code. They
> deviate from CL's LOOP (I could not bring myself to implement "BEING
> THE HASH-KEYS OF") in ways that are appropriate for JS semantics. Since
> PS LOOP was not fully compatible with CL's LOOP anyway, this is not much
> of a loss.
>
> (1) Added a FOR..OF clause that iterates through the keys of a JS object.
> This translates to JS's "for k in obj" loops and corresponds roughly
> to "loop for k being the hash-keys of" in CL.
>
>   (ps (loop :for k :of obj :do (foo k)))
>
>   "for (var k in obj) {
>       foo(k);
>   };"
>
> To bind both key and value, you specify a pair. (This is an icky bit
> of syntax but it feels like the least icky option of the ones I'm
> aware of.)
>
>   (ps (loop :for (k v) :of obj :do (foo k v)))
>
>   "for (var k in obj) {
>       var v = obj[k];
>       foo(k, v);
>   };"
>
> Destructuring works on the value position just like they do everywhere
> else, so you can unpack an array. (Note that in this example the key
> variable isn't needed so we can put NIL in there.)
>
>   (ps (loop :for (nil (v1 v2)) :of obj :do (foo v1 v2)))
>
>   "for (var _js23095 in obj) {
>       var _js23096 = obj[_js23095];
>       var v1 = _js23096[0];
>       var v2 = _js23096[1];
>       foo(v1, v2);
>   };"
>
> ... or named properties (by using keywords):
>
>   (ps (loop :for (nil (:hello :world)) :of obj :do (foo hello world)))
>
>   "for (var _js23097 in obj) {
>       var var23099 = obj[_js23097];
>       var hello23101 = var23099.hello;
>       var world23102 = var23099.world;
>       foo(hello23101, world23102);
>   };"
>
> Side note: To get the scoping right for parallel loop clauses is
> tricky because LOOP orders things "step test step test" where JS's FOR
> does "step step test test", meaning that the second step will be
> evaluated even if the first test fails. It got trickier with the
> introduction of FOR..OF, so I adopted a simpler implementation that
> has some advantages (much tighter generated loop code) but also some
> disadvantages: (a) it was a pain to support :INITIALLY clauses so I
> dropped them (I'd be surprised if anyone ever used it), and (b)
> complex loops sometimes require an extra temporary variable to track
> whether the loop is on its first iteration or not.
>
> (2) There is now a MAP..TO clause that works like COLLECT only instead
> of building an array, builds an object mapping keys to values:
>
>   (ps (loop :for str :in strs :map str :to (length str)))
>
>   "(function () {
>       var _js23109 = strs.length;
>       var map23110 = {  };
>       for (var _js23108 = 0; _js23108 < _js23109; _js23108 += 1) {
>           var str = strs[_js23108];
>           map23110[str] = str.length;
>       };
>       return map23110;
>   })();"
>
> (3) In FOR..ON, a BY term can now be an integer. For example, you can
> say "by 2" instead of "by #'cddr" which makes little sense for JS arrays:
>
>   (ps (loop :for (key val) :on pairs :by 2 :do (foo key val)))
>
>   "for (var _js23094 = pairs; _js23094.length > 0; _js23094 =
> _js23094.slice(2)) {
>       var key = _js23094[0];
>       var val = _js23094[1];
>       foo(key, val);
>   };"
>
> If BY doesn't get a number, it assumes it got a function:
>
>   (ps (loop :for (key val) :on pairs :by blah :do (foo key val)))
>
>   "for (var _js23112 = pairs; _js23112.length > 0; _js23112 =
> blah(_js23112)) {
>       var key = _js23112[0];
>       var val = _js23112[1];
>       foo(key, val);
>   };"
>
> Daniel
>
> _______________________________________________
> parenscript-devel mailing list
> parenscript-devel at common-lisp.net
> http://lists.common-lisp.net/cgi-bin/mailman/listinfo/parenscript-devel
>




More information about the parenscript-devel mailing list