guile-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

hygiene and macro-introduced toplevel bindings


From: Andy Wingo
Subject: hygiene and macro-introduced toplevel bindings
Date: Sun, 27 Feb 2011 22:37:42 +0100
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/23.2 (gnu/linux)

Hello all,

Andreas has been struggling with a nonstandard behavior of Guile's
recently, and we should discuss it more directly.

The issue is in expressions like this:

  (define-syntax define-accessor
    (syntax-rules ()
      ((_ getter setter init)
       (begin
         (define val init)
         (define getter (lambda () val))
         (define setter (lambda (x) (set! val x)))

  (define-accessor get-x set-x! 0)

The issue is, what happens when this expression is expanded?

Within a let or a lambda, it expands to an three internal definitions:
`val', `getter', and `setter', where `val' is only visible to within the
`getter' and `setter' procedures.

At the top level, it expands to three definitions: "val", the getter,
and the setter.  However in this case the "val" binding is global to the
module, and can be referenced by anyone.

This is what happens in Guile.  I know that some other Schemes do
different things.  Chez, as far as I understand it, binds "val" in the
module, but under a gensym'd name.  It can do this because its modules
are syntactic: the bindings in a module are not serialized to disk as
simple symbol-binding pairs, but rather the whole expansion-time ribcage
is also written out.  That's how I understand it anyway; I could be
getting things wrong there.

Anyway, in Guile our modules have always been first-class entities.  We
never intern gensym'd names in modules, because who would do that?  You
put a name in a module because you want to be able to name it, either
internally or externally, and gensym'd names don't make any sense
without some sort of translation table, and Guile's first-class modules
have no such table.

Furthermore, gensyms at the top-level have a cost that they do not have
lexically.  When you introduce a lexical binding in a macro and cause a
new name to be allocated to it, that binding only exists within the
scope of that form -- if the form is an expression, it exists during the
dynamic extent of that expression, and if it is a definition, its extent
is bound to the extent of the binding of some /other/ name---the
top-level name.

But when you introduce a generated name to the top-level, you create
some trash "val-12345543" binding which will always be there, and you
don't know why.  It can never be removed by normal means, because
top-level bindings are never removed, and its name is invisible to all
other code -- it has infinite extent.

And that's the thing that really bothers me about generated top-level
names: they are only acceptable if you plan on never changing your
mind.  You're not bothered by the trash name, because you'll never
expand that expression again.

So!  That's my rant.  But this is, even more than usual, a case in which
I could simply be wrong; so if you really want to defend generated
top-level names, now would be a great time to do so ;-)

Cheers,

Andy
-- 
http://wingolog.org/



reply via email to

[Prev in Thread] Current Thread [Next in Thread]