[plt-scheme] Confusion about lexical scoping, modules, and hygenic macros

From: Danny Yoo (dyoo at hkn.eecs.berkeley.edu)
Date: Wed Aug 17 17:50:50 EDT 2005

On Wed, 17 Aug 2005, Matthew Flatt wrote:

> > ;;;;;;
> > > (module m3 mzscheme
> >   (require (lib "contract.ss"))
> >   (require m1)
> >   (display (arrow -> this-is-from-m3))
> >   (newline))
> > repl-2:4:11: arrow: bad syntax in: (arrow -> this-is-from-m3)
> > ;;;;;;
> >
> > I think I understand what's causing this.  Somehow, during macro
> > expansion, the macro system's convinced that '->' is lexically bound,
> Yes. Within `m3', `->' is bound to a contract constructor by the import
> of "contract.ss". So the `->' in the use of `arrow' does not match the
> `->' in the definition.

Hi Matthew,

Thanks for the explanation; ok, so I was definitely confused.  I just have
to expand my understanding of "lexical binding" to treat modules in that
special way.

I always thought that lexical binding only meant then the code was
physically nested --- as with let --- but I see now that anything defined
bound at module-level should also be considered lexically bound within
that module.  (I think it's very obvious I'm still trying to graduate from
my "Intro to CS using Scheme"  days.  *grin*)

That makes sense, though the implications of it troubles me a bit; if I
understand it properly, it implies that the usage of syntax to represent
keywords can be a bit fragile unless we take precautions.

[reading more through the reference manual]

Ah, ok, I see!  So there is an extended version of syntax-case called
syntax-case* which accepts an arbitrary comparator for the pattern
literals.  So something like:

(module m1 mzscheme
  (provide (all-defined))
  (define-syntax (arrow stx)
    (define (symbolic-identifier=? x y)
      (eqv? (syntax-object->datum x)
            (syntax-object->datum y)))

    (syntax-case* stx (->) symbolic-identifier=?
      [(arrow -> x)
       (syntax/loc stx
         (list (quote x) 'arrowed!))])))

is a little uglier, but it allows me to define a reserved keyword in the
way I was expecting.  Let me check:

> (module m3 mzscheme
  (require (lib "contract.ss"))
  (require m1)
  (display (arrow -> this-is-from-m3))

> (require m3)
(this-is-from-m3 arrowed!)

Great!  That works much better.

Thanks again for your help!

Posted on the users mailing list.