[racket] Combining iteration and match

From: Stephen Chang (stchang at ccs.neu.edu)
Date: Tue Sep 3 13:10:38 EDT 2013

Like others have mentioned, my generic-bind package has the
functionality you want but is still in the experimental stages. For
small programs, the generic-bind for/_ forms are about as fast as the
current Racket implementation but expect 2-3x slowdown for larger
programs.

On Tue, Sep 3, 2013 at 10:54 AM, J. Ian Johnson <ianj at ccs.neu.edu> wrote:
> Asumu's suggestion is what I would use, since generic-bindings are currently unstable and not as performant as the idiomatic manual solutions.
> A difficult idiom to encode, however, is when you want to skip elements of the sequence that don't match, since you end up needing define forms between for-clauses, as well as some "doesn't match" value that you give to all would-be defined identifiers, and add #:unless (eq? first-match-id doesnt-match). Not only can you not even do that, getting it to work in the current for-forms would require extra allocation (say, putting all matching identifiers in a list, or #f if no match) and additional nesting of for forms, which ruins the convenience of the for/X macros. We can't express [(list a b) (in-list list-of-2lists)] as a special form that uses :do-in, since match shouldn't have to know how in-list decomposes its sequence.
>
> Instead, I would propose an extension of the for macro collection itself, such that the built looping structure is generalized to something like
>
> (let-outer-form ([outer-pattern outer-expr] ...)
>   outer-check
>   (let-pre-loop-form ([pre-loop-pattern pre-loop-expr] ...)
>     (let-loop-form loop ([loop-pattern loop-expr] ...)
>       (if pos-guard
>         (let-inner-form ([inner-pattern inner-expr] ...)
>           (if pre-guard
>               (let body-bindings
>                  (if post-guard
>                      (let-reloop-form ([reloop-pattern reloop-expr] ...)
>                        (loop loop-arg ...))
>                      done-expr))
>               done-expr))
>          done-expr))))
>
> Notice the new pre-loop and reloop binding opportunities. I've found myself wanting to compute some values that would otherwise have to be recomputed or hackily stored somewhere in order to use in loop-expr ... and loop-arg ...
>
> for has the following instantiation
> [let-outer-form let-values]
> [(outer-pattern ...) ((outer-id ...) ...)]
> [let-pre-loop-form let]
> [(pre-loop-pattern ...) ()]
> [(pre-loop-expr ...) ()]
> [let-inner-form let-values]
> [(inner-pattern ...) ((inner-id ...) ...)]
> [let-reloop-form let]
> [(reloop-pattern ...) ()]
> [(reloop-expr ...) ()]
>
> Now I say "something like" since this looping structure is built for the entire collection of for-clauses, and not just one. Thus, there needs to be a sensible protocol for special clause forms to be combined in the different generalized forms. We could have our own outer-form/pre-loop-form/let-loop-form/let-inner-form/let-body-form/let-reloop-form implementations such that this protocol is implementable in the generic binding library. The protocol I have in mind is that clauses with pattern (id ...) get collected into one let-values, whereas adjacent forms defined in a generic-binding way can choose to either be cascaded in a chosen form, or collected into a chosen form. Additionally, since we want the ability to say that non-matching isn't a failure, but a signal to continue, there is more we need to be able to do with the for forms; indeed, the above template lies a bit, since it does not mention the possibilities of #:when, #:unless, #:break or #:final, where the first t!
>  wo actually call loop with all the same accumulators, but with the sequence "stepped forward one." This changes the format of the let-X-form setup, since you would instead have a successful branch and a failing branch (consider a match fall-through clause either raising an error or calling the loop on the same accumulators with the stepped sequence(s)).
>
> This kind of overhaul is not something we can do externally to one of the most used, non-trivial collection of macros in the Racket code base. We also have to use a more conservative language to implement it, since it's so low on the language tower. Perhaps later this year I'll have time to submit a pull-request that opens the for macros up a bit more in this fashion, but I anticipate a heavy push-back from core developers due to its supreme importance to Racket's foundation.
>
> -Ian
>
> ----- Original Message -----
> From: "Asumu Takikawa" <asumu at ccs.neu.edu>
> To: "Konrad Hinsen" <konrad.hinsen at fastmail.net>
> Cc: users at racket-lang.org
> Sent: Tuesday, September 3, 2013 9:12:54 AM GMT -05:00 US/Canada Eastern
> Subject: Re: [racket] Combining iteration and match
>
> On 2013-09-03 11:32:23 +0200, Konrad Hinsen wrote:
>>   (for/list ([(list a b) some-sequence])
>>     a)
>
> I usually use `match-define`:
>
>   (for/list ([a+b some-sequence])
>     (match-define (list a b) a+b)
>     a)
>
> Can be slightly longer than just `match` for simple cases, but doesn't
> cause rightward drift.
>
> Cheers,
> Asumu
> ____________________
>   Racket Users list:
>   http://lists.racket-lang.org/users
>
> ____________________
>   Racket Users list:
>   http://lists.racket-lang.org/users


Posted on the users mailing list.