Okay, I see what's going on here. It's very subtle though, and probably deserves some explanation in split-for-body's documentation.
My first thought on seeing my non-fix version break here is that I can make split-for-body break the same way. The problem is that my non-fix separates the definition of fish? from the definitions of red? and blue?, which it depends on. I can make split-for-body separate them the same way, by putting a #:break or #:final clause in between the definition of fish? and the begin form. The problem with doing so is a subtle point about for loops that is only mentioned in the last sentence of the last paragraph of the documentation of for itself: "Among the bodys, besides stopping the iteration and preventing later bodyevaluations, a #:break guard-expr or #:final guard-expr clause starts a new internal-definition context." So that's what split-for-body is preserving, the boundaries between internal definition contexts. That's not at all what I had expected it was doing; I had no idea the body of a for loop constituted multiple such contexts. Anyway, thanks for the clarification, I now understand why abstractions over for loops need to use split-for-body. Carl Eastlund On Fri, Sep 6, 2013 at 12:38 PM, Matthew Flatt <mfl...@cs.utah.edu> wrote: > Sorry that I forgot to add the `let` while turning the code you sent > into a full example. Here's another try. > > #lang racket/base > (require (for-syntax racket/base > syntax/parse > syntax/for-body)) > > (define-syntax (for/print/good stx) > (syntax-parse stx > [(_ clauses . body) > (with-syntax ([([pre ...] [post ...]) (split-for-body stx #'body)]) > (syntax > (for clauses > pre ... > (printf "~v\n" (let () post ...)))))])) > > (define-syntax-rule (for/print/fixed/not clauses pre ... result) > (for clauses > pre ... > (printf "~v\n" (let () result)))) > > (for/print/fixed/not ([i 1]) > (define (fish? v) (or (red? v) (blue? v))) > (begin > (define (red? v) (eq? v 'red)) > (define (blue? v) (eq? v 'blue)) > (fish? i))) > > At Fri, 6 Sep 2013 12:30:17 -0400, Carl Eastlund wrote: > > You're proving that (let () ...) is necessary, which I have explicitly > > agreed with since the original email, but you have not yet demonstrated > > that split-for-body is necessary. Here is the fix I have described twice > > already, now explicitly put into the define-syntax-rule solution: > > > > (define-syntax-rule (for/print/fixed clauses pre .. result) > > (for clauses > > pre ... > > (printf "~v\n" (let () result)))) > > > > Carl Eastlund > > > > > > On Fri, Sep 6, 2013 at 12:25 PM, Matthew Flatt <mfl...@cs.utah.edu> > wrote: > > > > > > > > #lang racket/base > > > (require (for-syntax racket/base > > > syntax/parse > > > syntax/for-body)) > > > > > > (define-syntax (for/print/good stx) > > > (syntax-parse stx > > > [(_ clauses . body) > > > (with-syntax ([([pre ...] [post ...]) (split-for-body stx > #'body)]) > > > (syntax > > > (for clauses > > > pre ... > > > (printf "~v\n" (let () post ...)))))])) > > > > > > (define-syntax-rule (for/print/bad clauses pre ... result) > > > (for clauses > > > pre ... > > > (printf "~v\n" result))) > > > > > > ;; Try changing to for/print/bad: > > > (for/print/good ([i 1]) > > > (define (fish? v) (or (red? v) (blue? v))) > > > (begin > > > (define (red? v) (eq? v 'red)) > > > (define (blue? v) (eq? v 'blue)) > > > (fish? i))) > > > > > > > > > At Fri, 6 Sep 2013 12:17:56 -0400, Carl Eastlund wrote: > > > > Right, that's the issue with needing the (let () result) in my > > > > define-syntax-rule version. I still didn't need split-for-body, > which > > > > doesn't guarantee there are no definitions in the post ... part. > All it > > > > guarantees to eliminate are #:final and #:break. > > > > > > > > Carl Eastlund > > > > > > > > > > > > On Fri, Sep 6, 2013 at 12:09 PM, Matthew Flatt <mfl...@cs.utah.edu> > > > wrote: > > > > > > > > > The issue is `begin` splicing. The `result` form could be a `begin` > > > > > form that contains definitions that are referenced by a preceding > > > > > forms. > > > > > > > > > > For example, given > > > > > > > > > > (define (fish? v) (or (red? v) (blue? v))) > > > > > (begin > > > > > (define (red? v) ....) > > > > > (define (blue? v) ....) > > > > > 5) > > > > > > > > > > With `begin` splicing, that turns into > > > > > > > > > > (define (fish? v) (or (red? v) (blue? v))) > > > > > (define (red? v) ....) > > > > > (define (blue? v) ....) > > > > > 5 > > > > > > > > > > which is different than > > > > > > > > > > (define (fish? v) (or (red? v) (blue? v))) > > > > > (let () > > > > > (define (red? v) ....) > > > > > (define (blue? v) ....) > > > > > 5) > > > > > > > > > > At Fri, 6 Sep 2013 11:15:50 -0400, Carl Eastlund wrote: > > > > > > Is this function ever particularly necessary? Its intended use > > > seems to > > > > > be > > > > > > like so: > > > > > > > > > > > > (define-syntax (for/print stx) > > > > > > (syntax-parse stx > > > > > > [(_ clauses . body) > > > > > > (with-syntax ([([pre ...] [post ...]) (split-for-body > #'body)]) > > > > > > (syntax > > > > > > (for clauses > > > > > > pre ... > > > > > > (printf "~v/n" (let () post ...)))))])) > > > > > > > > > > > > That way any #:break or #:final from the body ends up in pre ..., > > > where > > > > > the > > > > > > enclosing for loop will interpret them, and post ... will only > > > include > > > > > > normal definitions and expressions. > > > > > > > > > > > > But it seems to me there's a much easier way that should always > work: > > > > > > > > > > > > (define-syntax-rule (for/print clauses pre ... result) > > > > > > (for clauses > > > > > > pre ... > > > > > > (printf "~v\n" result))) > > > > > > > > > > > > This not only puts all #:break and #:final clauses in pre ..., it > > > should > > > > > > guarantee result is an expression. Perhaps one should still > write > > > (let > > > > > () > > > > > > result) in case result is (begin defn expr), but that's still > simpler > > > > > than > > > > > > using split-for-body. > > > > > > > > > > > > My question is -- have I overlooked some clever subtlety here > that > > > makes > > > > > > split-for-body necessary, or is it usually easier to just > decompose > > > pre > > > > > ... > > > > > > result rather than bothering with split-for-body? > > > > > > > > > > > > Carl Eastlund > > > > > > _________________________ > > > > > > Racket Developers list: > > > > > > http://lists.racket-lang.org/dev > > > > > > > > > > > > > > > > > >
_________________________ Racket Developers list: http://lists.racket-lang.org/dev