[plt-scheme] Macro techniques for implementing lexical scoped binding constructs
--- Jens Axel Søgaard <jensaxel at soegaard.net> wrote:
> Is there a "canoncial" way of implementing lexical scoped
> binding constructs "from scratch"?
Yes: as you mentioned, syntax-parameters are good for this.
> Below I use the "names" module to keep track of which
> names are bound in the current lexical context. It maintains
> a standard ribcage, where the spine is cons-cells and the
> individual ribs are module-identifier-mappings from identifiers to
> values.
>
> When a new scope is entered, a new mapping is consed onto the list,
> and it is removed when the scope is left
>
>
> As an example, a silly macro letn is implemented:
>
> (letn ((x 1))
> (letn ((y 2))
> (+ (ref x) (ref y))))
> ; => 3
>
> (letn ((x (begin (display 'hello!) 1)))
> (+ (ref x)
> (ref x)))
> ; displays: hello! hello!
> ; => 2
>
>
> (define-syntax (letn stx)
> (syntax-case stx ()
> [(_ ((name val) ...) body ...)
> (begin
> ; new scope is entered, add new mapping
> (register-new-level)
> ; bind the variables
> (for-each register
> (syntax->list #'(name ...))
> (syntax->list #'(val ...)))
> (begin0
> ; expand the body (now the variables are bound)
> (local-expand #'(begin body ...)
> (syntax-local-context)
> '() #f)
> ; to leave the scope, we remove outmost mapping
> (register-remove-level)))]))
>
> What bothers me here is the use of local-expand.
>
> Should it bother me?
Yes, it should. Generally, using local-expand for "deep" expansion
should make you nervous about both the correctness and the
performance of your macros.
> Is there alternatives? (syntax-parameters perhaps - but how?)
Here's how to do it with syntax parameters:
(require (lib "stxparam.ss"))
(require-for-syntax (lib "stxparam.ss"))
(define-syntax-parameter the-env null)
(define-syntax (letn stx)
(syntax-case stx ()
[(letn ([name value] ...) . body)
(with-syntax ([(var ...) (generate-temporaries #'(name ...))])
#'(let ([var value] ...)
(let-syntax ([old-env
(syntax-parameter-value #'the-env)])
(syntax-parameterize
([the-env
(append (list (cons #'name #'var) ...)
(syntax-local-value #'old-env))])
. body))))]))
(define-syntax (ref stx)
(syntax-case stx ()
[(ref name)
(let ([env (syntax-parameter-value #'the-env)])
(let loop ([env env])
(cond [(pair? env)
(if (module-identifier=? #'name (car (car env)))
(cdr (car env))
(loop (cdr env)))]
[else
(raise-syntax-error 'ref "unbound name"
#'name)])))]))
The first time I tried this, I wrote the syntax-parameterize like
this:
(syntax-parameterize
((the-env (append --- (syntax-parameter-value #'the-env))))
---)
There seems to be a bug in the implementation of syntax-parameterize
that causes that to raise an "identifier out of context" error. So I
changed the code to retrieve the old value of the parameter outside
of the parameterization, and it worked.
This implementation passes your tests.
Ryan
>
> _________________________________________________
> For list-related administrative tasks:
> http://list.cs.brown.edu/mailman/listinfo/plt-scheme
>