bug-lilypond
[Top][All Lists]
Advanced

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

Re: New read/eval Scheme syntax inconsistent in handling existing code


From: David Kastrup
Subject: Re: New read/eval Scheme syntax inconsistent in handling existing code
Date: Sun, 04 Dec 2011 13:20:12 +0100
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/24.0.90 (gnu/linux)

Valentin Villenave <address@hidden> writes:

> On Sun, Dec 4, 2011 at 9:38 AM, David Kastrup <address@hidden> wrote:
>> Apart from the rest of this ugly contraption,
>
> You mean, uglier than using eval-string? I'm not sure what a less ugly
> way of doing that would look like. (Perhaps using hooks, but I have
> yet to master that.)

I don't see why you would be using make-parameter at all, and why you
would be creating a parameter of type _letter_ when you actually _need_
a symbol.  Then write a macro around it that needs to do some partial
evaluation using primitive-eval in order to evaluate one half of a
construct in advance but not the other and you already have one added
layer of diddling with evaluation order using macros...

>> there is no reason whatsoever why you are calling the _internals_ of
>> making music functions here instead of define-music-function itself.
>
> Absolutely none whatsoever. That was pure uncalled-for geekness from
> me (at the time, it also allowed me to learn more about the
> music-function internals).

I protest in the name of geeks against this characterization.

> It isn't. defmacro certainly is a cleaner way to do it. That was just
> to show you an example of a case where eval-string was useful to me,
> re. our previous conversation.

But it doesn't do anything you could not have done exactly the same with
the old code without any difference in the bag of tricks otherwise.

I don't get it.  It's like you want to put a nail into a wall, and miss
with a hammer, so you take a shoe and complain to the producer of the
hammer that the nails are really hard to drive using a shoe.

>
>> Or put differently: the principal difference of # and $ is that $
>> interprets the resulting expression and generates a token corresponding
>> to its type (or in this particular case, none at all).  The premature
>> evaluation is a _side-effect_ of this difference.
>
> Thanks. I think I get it now. I was confused between the _LilyPond_
> parser and the guile interpreter; when you said $(...) expressions
> were evaluated instantly I misunderstood and thought you were
> referring to the former.

They are evaluated in the lexer.  In contrast, # is _read_ in the lexer
and evaluated in the parser.

Or, to quote from the manual "Input variables and Scheme":

traLaLa = { c'4 d'4 }

#(define newLa (map ly:music-deep-copy
  (list traLaLa traLaLa)))
#(define twice
  (make-sequential-music newLa))

{ \twice }

   This is actually a rather interesting example.  The assignment will
only take place after the parser has ascertained that nothing akin to
`\addlyrics' follows, so it needs to check what comes next.  It reads
`#' and the following Scheme expression _without_ evaluating it, so it
can go ahead with the assignment, and _afterwards_ execute the Scheme
code without problem.

   The above example shows how to `export' music expressions from the
input to the Scheme interpreter.  The opposite is also possible.  By
placing it after `$', a Scheme value is interpreted as if it were
entered in LilyPond syntax.  Instead of defining `\twice', the example
above could also have been written as

...
{ $(make-sequential-music (list newLa)) }

   You can use `$' with a Scheme expression anywhere you could use
`\NAME' after having assigned the Scheme expression to a variable NAME.
This replacement happens in the `Lexer', so Lilypond is not even aware
of the difference.

   One drawback, however, is that of timing.  If we had been using `$'
instead of `#' for defining `newLa' in the above example, the Lexer
would have evaluated the Scheme code right away in order to figure out
the kind of the next token before Lilypond would have had a chance for
executing the assignment.  Consequently, the Scheme definition would
have failed because `traLaLa' would not yet have been defined.  As a
rule, using `#' rather than `$' whenever it is allowed will cause fewer
surprises.

>> If would appear that you consider the side-effect more important than
>> the main effect, and have for that reason decided to be smarter than
>> convert-ly and replace # everywhere with $.
>
> Well, you have to take into account where we come from: I'm not used
> to convert-ly being smarter than me :-)

It certainly isn't.  But it may have better teachers at times.

>> Looks like we have lots of fun ahead.  Do me a favor and from time to
>> time add material to Lilypond's documentation that would have helped
>> you avoid this kind of lock-in hack programming.  Once you know how
>> to avoid it.
>
> That's the whole point of our conversation so far: I've already
> identified two eligible regtests (the ly:parser-include-string thing
> and the eval-string thing).

Uhm, "avoid this kind of lock-in hack programming", not support it
better.  The regtests are for features that should not stop working
without good reason.  This includes ly:parser-include-string and
eval-string.  But that does not imply that what you use them for is a
good idea.

> On a totally unrelated note (since we're talking about Scheme
> evaluation), if I may quote a question that I discussed privately with
> Neil last year:
>
> On Mon, Dec 6, 2010 at 6:31 PM, Neil Puttock <address@hidden> wrote:
>> This caught my eye:
>> http://git.savannah.gnu.org/cgit/opus-libre.git/commit/?id=f149327afc41d869e34c29cf32b415254542ca9e
>>
>> (define bar '(hello))
>> (define foo (if (pair? bar) (display "Hello World!")))
>>
>> This reminds me of the problem we have with identifiers: how can we
>> document them automatically?  A really ineffecient method would be to
>> keep an alist handy with (key . docstring) pairs, but I think the only
>> way around this would be to extend the use of \description (though
>> this would require all identifiers to be sequential blocks, e.g.,
>>
>> hideNotes =
>> {
>>  \description "..."
>>  ...
>> }

describeMusic =
#(define-music-function (parser location description music) (string? ly:music?)
  (set! (ly:music-property music 'description) description)
  music)

hideNotes =
\describeMusic "This hides notes"
{
  ...
}

-- 
David Kastrup



reply via email to

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