unofficial mirror of guile-devel@gnu.org 
 help / color / mirror / Atom feed
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/>



  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).