[racket] Again on bindings visibility in eval

From: Markku Rontu (markku.rontu at iki.fi)
Date: Thu Jul 14 14:58:55 EDT 2011

Hey Thomas,

On Thu, Jul 14, 2011 at 8:21 PM, Thomas Chust <chust at web.de> wrote:

> Markku Rontu wrote:
> > [...]
> > I'm sure the section 11 does go through the relevant issues, but doesn't
> > feel like a good introduction to the matter. I can theorise that I can
> > manipulate these syntax objects with plain old functions but nowhere
> > does it seem to show a complete useful case. What I'm missing is an
> > example that contains all the bits but that is not as complicated like
> > the Racket sources themselves. Maybe I just didn't find it?
> > [...]
>
> Hello Markku,
>
> as others have already written it is easier and more rewarding to use
> syntax-case and other syntax related libraries available in Racket to
> write complex macros rather than to manipulate syntax objects by hand.
> Probably that is the case why the former functionality is more
> prominently featured in the documentation and the latter is left for the
> experts that are able to understand everything after glancing once at a
> dry reference document ;-)
>
> Thanks, I'm very much against dry reference documents as marketing :-)
Overall, I'm very happy with the documentation in Racket, though I often
wish there were more short examples scattered around.

I think the problem is the step between basic macrology and mastery. I'm
somewhere in there wandering. And wondering why it's so difficult when it
should not be?


> Anyway, there is nothing magic about syntax objects, they are just
> wrappers around regular S-expression datums that add context
> information. To unwrap the data in a syntax object you use syntax-e.
> However, the data structure you get is likely to contain more syntax
> objects since context information is attached to every cons cell, every
> vector entry, every hash table value, etc.
>
> Well one problem is that all these syntax objects have introduced a new
language, with accessors and functions that are completely new that must be
learnt. And programs using these new functions look quite different to
standard data structure (i.e., cons cell) manipulation. At least so far it
looks like that to me. Clojure gives me vectors and hash-maps, so no new
data structure but untyped data which I can manipulate trivially. There is
no reason why it can't contain all the properties, source location,
certificate etc. information. Just a different approach. I can't say
approach which is better :-)


> Therefore there are additional handy tools to access syntax objects: If
> you want to discard lexical context entirely and just turn a syntax
> object into data you can use syntax->datum, if you want to turn syntax
> representing a list into a list of syntax objects you can use syntax->list.
>
> For the other direction, to wrap some data into a syntax object, you use
> datum->syntax and supply an existing syntax object to provide the
> context for the new synthetic expression. In addition there is a
> quasiquote analogue for syntax objects that you can use.
>
> Using the syntax quasiquote notation respects hygiene by default, but
> unquoting some manually constructed object with the right context you
> can selectively break hygiene. Since you don't have to bother about
> manual generation of fresh identifiers and proper namespace references
> at all and still get hygienic behaviour by default, you can focus on the
> few points where you want to break hygiene. Therefore I even claim that
> this programming style will still be easier and less cluttered with
> boilerplate code in Racket than in Common Lisp or Clojure unless you
> disregard hygiene issues entirely.
>
> I'll give you an example implementation of the classical anaphoric if
> macro that selectively introduces an identifier called `it' bound to the
> result of the test expression into the lexical context of the
> conditional's then branch and apart from that behaves just like the
> normal `if':
>
>  #lang racket/base
>  (require
>   (for-syntax racket/base))
>
>  (define-syntax (aif stx)
>    (cond
>      ;; We need one "special" command to unwrap the syntax object
>      ;; holding the whole statement ...
>      [(syntax->list stx)
>       => (lambda (forms)
>            (if (= (length forms) 4)
>                (let ([test-expr (cadr forms)]
>                      [then-expr (caddr forms)]
>                      [else-expr (cadddr forms)])
>                  ;; ... and one "special" command to introduce the
>                  ;; identifier `it' into the correct context ...
>                  (let ([$it (datum->syntax then-expr 'it)])
>                    ;; ... however other languages would need a bunch
>                    ;; of magical incantations to make cond, else and
>                    ;; λ refer to the right things reliably, which we
>                    ;; get for free.
>                    #`(cond
>                        [#,test-expr => (λ (#,$it) #,then-expr)]
>                        [else #,else-expr])))
>                (raise-syntax-error
>                 #f "expected test, then and else clauses" stx)))]
>      [else
>       (raise-syntax-error #f "expected list of expressions" stx)]))
>
>  (provide
>   aif)
>
> For comparison, I also include an implementation to the same effect but
> using a nicer encapsulation of the break of hygiene, thanks to a helper
> module provided with Racket:
>
>  #lang racket/base
>  (require
>   (for-syntax racket/base srfi/26)
>   racket/stxparam)
>
>  ;; We declare the identifier of the special variable explicitly and
>  ;; we also export it later
>  (define-syntax-parameter it
>    (cut raise-syntax-error
>     #f "implicit value used outside anaphoric if" <>))
>
>  (define-syntax aif
>    (syntax-rules ()
>      [(aif test-expr then-expr else-expr)
>       (cond
>         [test-expr
>          => (λ (v)
>               ;; The syntax-parameterize form takes care of making the
>               ;; special variable refer to the right thing in the
>               ;; context of the macro expasion
>               (syntax-parameterize ([it (syntax-id-rules () [it v])])
>                 then-expr))]
>         [else
>          else-expr])]))
>
>  (provide
>   it aif)
>
> I hope the quick summary and examples are helpful :-)
>
> Ciao,
> Thomas
>
>
> --
> When C++ is your hammer, every problem looks like your thumb.
>


Thanks, I'll have a look later for the details. I'm familiar with
syntax-case because I often break hygiene in my macros to reduce repetition
or to make the interaction in a DSL more state-machine -like. I'm however
sure that somebody else will find these discussions and examples useful too
:-)

-Markku
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.racket-lang.org/users/archive/attachments/20110714/f1ddb78b/attachment.html>

Posted on the users mailing list.