[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Two optional arguments
Re: Two optional arguments
Tue, 14 Jul 2020 22:57:47 +0200
Gnus/5.13 (Gnus v5.13) Emacs/28.0.50 (gnu/linux)
Urs Liska <firstname.lastname@example.org> writes:
> Hi all,
> is it correct that you can't write a music function with two optional
> arguments in a row?
No, it isn't correct. But you know yourself, quoting the respective
passages from the manual.
> If so:
> * is that on purpose?
> * is that inevitable?
I don't know what you call "inevitable".
> * is there a workaround for my use case (below)?
> Consider this function
> twoOpts =
> #(define-void-function (one two three)((symbol? 'foo) (number? 5)
> (ly:message "one: ~a
> two: ~a
> three: ~a" one two three))
> and this call
> This works correctly, and when I comment out the 4 it properly picks
> the default value 5.
> However, when I comment out both or only the first value it fails with
> error: wrong type for argument 3. Expecting string, found 4
That is an incorrect description. This only happens when you comment
out only the first value. When you comment out both optional values,
the first value that is seen is "bar" which is a valid value for a
symbol. If you instead write #"bar" instead, this can only become a
string argument and not a symbol and consequently both optional
arguments are replaced by their default.
> This behavious is consistent with the extending manual
> "Once an optional argument predicate does not match an argument,
> LilyPond skips this and all following optional arguments, replacing
> them with their specified default, and ‘backs up’ the argument that did
> not match to the place of the next mandatory argument. Since the backed
> up argument needs to go somewhere, optional arguments are not actually
> considered optional unless followed by a mandatory argument."
> So in
> The 4 doesn't match the first symbol? predicate, so all optional
> arguments receive their defaults and the 4 is "backed-up" to the next
> mandatory argument which happens to be a string? that now fails.
> It does work (also consistent with the docs) when each optional
> argument is followed by its "own" backup mandatory argument.
> So far, so bad. What I don't understand is why in case of a missing
> optional argument (detected by a failed type check) LilyPond has to
> skip "this and all following optional arguments". Couldn't LilyPond
> just skip "this" and back up the failed argument to the next (mandatory
> *or* optional) argument to cascade through all available arguments.
That would be a complete nightmare because LilyPond's large number of
default conversions (which you already got hit with when writing
\twoOpts "bar" which then interprets "bar" as a symbol) would very
likely make _some_ match in an unexpected place. By only checking
against a single optional argument, when things blow up around your
ears, it at least happens in a somewhat predictable place.
> In the call
> \twoOpts 4 "bar"
> * 4 wouldn't match symbol?
> * so one would get the default value 'foo
> * then the 4 is checked against the next predicate, which works
> * so two gets the 4
> * finally three gets "bar"
> In the call
> \twoOpts "bar"
> * "bar" wouldn't match symbol?
> * so one gets the default 'foo
> * "bar" is then checked against the next predicate, which fails too
> * so two gets the default 5
> * finally "bar" matches the mandatory argument's predicate
> I totally understand that it is absolutely necessary to have an
> unambiguous order of predicates when optional arguments are involved,
> but couldn't it be possible to have multiple optional ones following
> after another.
It would rarely work as expected. Also what would
then mean? Omitting only the first optional argument? That would be a
significant change in meaning. Omitting both? Why?
> I would really like to write a function with a symbol? and a
> ly:context-mod? argument and have both of them optional.
> Is there any possible workaround currently?
I have no idea what you call a "workaround". You can always use a
mandatory argument and use some special value to indicate special
> Would this be a legitimate feature request?
> Is there anything I've overlooked that makes this impossible?
I would very strongly discourage messing with that part of the design.
It has taken a long time (basically from 2.14 to 2.20) to make all
aspects of this design work dependably, not just in terms of it working
in a logical manner and as described, but also in terms of being
predictable and matching a large set of use cases (a whole lot of what
now is implemented in terms of music functions was hardwired syntax
Changes of the "I know, I'll just change the parser semantics for that
particular use case I care about" kind will very likely lead to a
cascading sequence of consequences that have no logical (let alone
programmatic) solution and will be discovered by and by.