From: Andreas Rottmann <a.rottmann@gmx.at>
To: Andy Wingo <wingo@pobox.com>
Cc: guile-devel <guile-devel@gnu.org>
Subject: Re: hygiene and macro-introduced toplevel bindings
Date: Mon, 28 Feb 2011 01:15:45 +0100 [thread overview]
Message-ID: <87bp1xui8e.fsf@vir.lan> (raw)
In-Reply-To: <m3ipw5rwex.fsf@unquote.localdomain> (Andy Wingo's message of "Sun, 27 Feb 2011 22:37:42 +0100")
Andy Wingo <wingo@pobox.com> writes:
> 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)
>
This example serves to illustrate the issue, but I want to make clear
that there are situations where one cannot work around "cleanly" around
this issue -- in the above example, one could use `define-values' to
define `setter' and `getter', and demote `val' into a `let' form inside
the `define-values' expression -- when the `setter' and `getter'
are macros, this is not possible.
> 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.
>
Sorry, I don't understand the part about the translation table, could
you clarify?
I agree that it makes no sense to allocate a named binding in the module
for `val', be it under a gensym'ed name, or just as `val'. The first is
bad because of the cost, as you note below, and the latter is bad
(worse, IMO) since it breaks encapsulation of the macro -- consider
this:
(define-accessor (get-foo set-foo! #f))
(define-accessor (get-bar set-bar! #f))
With the current psyntax implementation in Guile, this will lead to two
definitions of `val' inside the same module. Ideally, Guile would
allocate an "anonymous binding" inside the module -- a binding that has
only a location, and lacking a visible name. I have a vague idea how to
pull such a thing of:
During each macro expansion, create a lexical environment, and put all
hygeniencally renamed bindings (such as `val' in the above example) into
that environment. For the above example, this would already be enough;
the closures for `getter' and `setter' would refer to that environment,
and so it's kept alive as long as those don't get undefined/redefined.
If `getter' and `setter' were macros, one would have to put that
environment in a slot in the macro transformer bindings.
I know the above is quite hand-wavy, and I have actually no idea how
difficult such a thing would be to implement, but it might be possible,
even with first-level modules, to avoid the costs of gensym'd top-level
bindings without breaking hygiene/encapsulation. I guess it would even
be advantageous in terms of speed, as I guess lexical environment access
is faster than referring to top-level bindings by name(?).
> 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 ;-)
>
Well, I'm not defending generated top-level names but arguing for
preserving encapsulation/hygiene for macros ;-). I wonder what you (and
others) think about my idea as outlined above -- could such a thing
possibly work?
Regards, Rotty
--
Andreas Rottmann -- <http://rotty.yi.org/>
next prev parent reply other threads:[~2011-02-28 0:15 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-02-27 21:37 hygiene and macro-introduced toplevel bindings Andy Wingo
2011-02-27 22:02 ` Hans Aberg
2011-02-28 0:15 ` Andreas Rottmann [this message]
2011-02-28 21:28 ` Andy Wingo
2011-02-28 21:49 ` Noah Lavine
2011-03-08 22:33 ` Andy Wingo
2011-02-28 22:32 ` Ludovic Courtès
2011-03-08 22:37 ` Andy Wingo
2011-03-09 9:33 ` Hans Aberg
2011-03-09 20:14 ` Andy Wingo
2011-04-04 13:48 ` Hans Aberg
2011-04-01 8:52 ` Andy Wingo
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.gnu.org/software/guile/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87bp1xui8e.fsf@vir.lan \
--to=a.rottmann@gmx.at \
--cc=guile-devel@gnu.org \
--cc=wingo@pobox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).