[Top][All Lists]

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

Re: Anything better for delayed lexical evaluation than (lambda () ...)?

From: David Kastrup
Subject: Re: Anything better for delayed lexical evaluation than (lambda () ...)?
Date: Tue, 13 Dec 2011 23:23:28 +0100
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/24.0.92 (gnu/linux)

Andy Wingo <address@hidden> writes:

> On Tue 13 Dec 2011 17:54, David Kastrup <address@hidden> writes:
>>> Am I missing something?
>> Performance, space, simplicity, robustness.  Compiling five closures
>> that do nothing except accessing a single variable each is a bit
>> wasteful.
> Sure.
> Let me see if I finally understand the issue here:
> You have a function:
>   (define-music-function (foo bar)
>     (ly:something? bar)
      ^^^^^^^^^^^^^^^^^^^ Those are predicates, not arguments.
>     #{ /la /la /la
>        $bar $bar $bar
>        #(scheme-expression!)

Both # and $ come before Scheme expressions.  # is the regular type
visible in the parser, and has, for example, the property that it will
always serve as exactly one argument of a music function.  $ is an
"immediate" type that gets camouflaged as a Lilypond token right in the
lexer.  Using $ for assignments or less static cases than above is prone
to surprises since the parser generally operates with one token of
lookahead, and $ has to be evaluated before a token even exists.  In
contrast, # is read in the lexer and evaluated in the parser.

So basically the Guile relevant angle of the two is identical.

>        /ok }#)

> Before, you could turn the #{}# into a lambda

No, it was turned into a reader expansion.  No closure.  The reader
expansion had as constant contents the #{ ... #} contents as a string,
and a capture of the current lexical environment.  When this got
executed, the parser is let loose on the #{ ... #} contents, and when
encountering $ or #, it evaluates them like it would do outside of #{
... #} except for using a local-eval in the captured environment.

In either case, the Scheme reader is used for skipping over the sexp
following $ and #.  That is still done, but instead of throwing the read
sexp away, we now put it in a lambda together with its position in the
string (unless it is an obvious constant).  When evaluating # or $ at
runtime, we first check the alist of positions to lambdas, and if we
find a record of that position, evaluate a call to the recorded lambda
instead of the read expression.

Of course, this is not good for producing lvalues (pardon the Cism).

> and get at the $vars and evaluate the #(expressions) in the
> procedure-environment of the lambda.  Now, you have to munge around in
> the expression and, in this case, produce 4 closures: (lambda () bar),
> 3 times, and (lambda () (scheme-expression!)).
> Is that right?

Yes, that's what we currently do.  I don't know whether Scheme will be
smart enough to use the same memoization for all lambdas in
(list (cons 1 (lambda () bar)) (cons 5 (lambda () bar)) (cons 13 (lambda
      () bar)))
And it is a nuisance that one can't cons that thing together in the
reader and just quote it at runtime, because the read extension works in
the wrong lexical environment to produce the right kind of lambda.  One
could at best use two lists instead of an alist, and at least code the
position list as a single constant.

David Kastrup

reply via email to

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