lilypond-user
[Top][All Lists]
Advanced

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

Re: Having trouble understanding optional and variable amount of argumen


From: Stefano Troncaro
Subject: Re: Having trouble understanding optional and variable amount of arguments
Date: Thu, 8 Mar 2018 03:26:44 -0300

First of all, I apologize for the delayed response, I wanted to write back earlier but I couldn't find the time to delve into your last suggestions. Thank you for the useful replies!

@Harm
I don't understand most of what the code you provided is doing, only that it works. It's far too advanced for me, so (unfortunately) dissecting it would require much more time than what I currently have. It looks interesting though, I hope I can study it in more detail later.

I did manage to look a little bit into macros, and I have a question if you don't mind. This chapter of the guile documentation explains about define-syntax and syntax-rules. They appear to work but generate an error message about wrong number of arguments to a music function. Is that module not usable in Lilypond?

Example that works:
\version "2.19.80"

#(define-macro (test conditional true false)
   `(if ,conditional
        ,true
        ,false))

#(test #t (display "I'm true!\n") (display "I'm false!\n"))
Example that works but creates an error, stopping the file from compiling:
\version "2.19.80"
#(use-syntax (ice-9 syncase))

#(define-syntax test
   (syntax-rules ()
     ((test conditional true false)
      (if conditional
          true
          false))))

#(test #t (display "I'm true!\n") (display "I'm false!\n"))

%%% Creates the output:
%{
file.ly:5:2: error: GUILE signaled an error for the _expression_ beginning here
#
 (define-syntax test
I'm true!
Wrong number of arguments to #<Music function #<procedure #f (arg)>>
%}

@Urs
I looked into your examples and \with blocks are very useful.

You said earlier that you were thinking about how to make it so that the context-mod could have required arguments, default values for missing ones, and even predicates. I was thinking that context-mod->props could be made to accept this information as an optional argument. Then it can return a 'curated' list of props or raise warnings/errors. That I think shouldn't be difficult to do. Although I'm undecided on what would be a convenient way of storing the 'requirement data'. The obvious one to me is an alist with a structure like this: `((key1 . (required . #t)) (key2 . ((default . 9) (pred . ,number?))) ...), but I'm not sure. What do you think?

2018-03-03 11:34 GMT-03:00 Thomas Morley <address@hidden>:
2018-03-01 18:31 GMT+01:00 Stefano Troncaro <address@hidden>:
> I didn't know about \default or the dot/comma separated number/symbol lists!
> I can see those being useful in some circumstances. I was thinking about
> cases where an undefined amount of things different than symbols or numbers
> are required, and the closest I can imagine is chaining functions to create
> the illusion of a variable amount of arguments, like this:
>>
>> \version "2.19.80"
>>
>> #(define (end-list? obj)
>>    (and (list? obj)
>>         (let ((item (last obj)))
>>           (and (symbol? item)
>>                (equal? "end" (symbol->string item))))))
>>
>> end = #(list 'end)
>>
>> #(define (el->curated-el el)
>>    (delete 'end el))
>>
>> untilEnd =
>> #(define-void-function (proc el) (procedure? end-list?)
>>    (let ((curated-el (el->curated-el el)))
>>      (for-each
>>       (lambda (elem)
>>         (proc elem))
>>       curated-el)))
>>
>> selfAppending =
>> #(define-scheme-function (e-l) (end-list?)
>>    (let ((self-input (list (cons 1 2) (cons 3 4))))
>>      (append self-input e-l)))
>>
>> selfAppendingInput =
>> #(define-scheme-function (input e-l) (scheme? end-list?)
>>    (append (list input) e-l))
>>
>> \relative c'' {
>>   c d e f
>>   \untilEnd #pretty-print
>>     \selfAppending
>>     \selfAppendingInput #'(some useful input?)
>>     \selfAppendingInput #selfAppending
>>     \selfAppending
>>     \end
>>   g a b c
>> }
>
> This structure just happens to work for something I'm trying now but I can
> see it being too narrow in general.
>
> @Urs, I not familiar with \with blocks, I'll take a look at the oll-core
> code and experiment a bit with it. Maybe I'll be able to help.
>
> 2018-03-01 4:55 GMT-03:00 David Kastrup <address@hidden>:
>>
>> Stefano Troncaro <address@hidden> writes:
>>
>> > Thank you! I see that this is not an option then. Also, I now understand
>> > why I couldn't make the optional arguments work, since I always left
>> > them
>> > for last.
>> >
>> > Do you know if it is possible to have a flexible amount of optional
>> > arguments that appear before the last mandatory one? Say, for example
>> > (define-music-function (arg1 args music) (number? ??? ly:music?) where
>> > arg1
>> > and music are mandatory, and basically everything between arg1 and the
>> > next
>> > music _expression_ is compacted into a list and accessed as args in the
>> > body
>> > of the function. Not with that syntax necessarily, but something that
>> > allows for that kind of usage?
>>
>> You know that a number or symbol list can be entered as a
>> comma-separated list?
>>
>> --
>> David Kastrup

Hi Stefano,

I was always fine with one optional list?-predicate (this may ofcourse
be an alist) and sorting/processing this list in the body of the
music-function.
Or things like (lambda (arg . rest) ...) in some cases or the
comma-separated list (as already mentioned).

If you really want to go for a music-function with arbitrary arguments
here some thoughts.

Beside the result has still limitations:
- the amount of possible arguments has to be specified (ofcourse you
can go for something like 20, which should really be enough (currently
5 are defined)
- all those arguments have to be of kind (not (ly:music ...)), which
makes it impossible to enter a second music-argument.
- maybe more, it's not tested beyond the given examples
I expect David K will point out more weaknesses ... lol

I deleted the not matching doc-strings from define-syntax-function and
define-music-function.
Look into music-functions.scm to read them.

\version "2.19.65"

#(defmacro-public define-my-syntax-function
                  (args-amount type args signature . body)
  (define (has-parser/location? arg where)
    (let loop ((arg arg))
      (if (list? arg)
          (any loop arg)
          (memq arg where))))
  (define (currying-lambda args doc-string? body)
    (if (and (pair? args)
             (pair? (car args)))
        (currying-lambda (car args) doc-string?
                         `((lambda ,(cdr args) ,@body)))
        (let* ((compatibility? (if (list? args)
                                   (= (length args) (+ 2 (length signature)))
                                   (and (pair? args) (pair? (cdr args))
                                        (eq? (car args) 'parser))))
               (realargs (if compatibility? (cddr args) args)))
          `(lambda ,realargs
             ,(format #f "~a\n~a" realargs (or doc-string? ""))
             ,@(if (and compatibility?
                        (has-parser/location? body (take args 2)))
                   `((let ((,(car args) (*parser*)) (,(cadr args) (*location*)))
                       ,@body))
                   body)))))

  (let ((docstring
         (and (pair? body) (pair? (cdr body))
              (if (string? (car body))
                  (car body)
                  (and (pair? (car body))
                       (eq? '_i (caar body))
                       (pair? (cdar body))
                       (string? (cadar body))
                       (null? (cddar body))
                       (cadar body))))))

    (let ((new-args
            (map
              (lambda (i)
                (string->symbol (format #f "~a-~a" (car args) i)))
              (iota args-amount 1 1))))
      (set! args (append new-args (cdr args)))
      (set! signature
            (append
              (make-list args-amount (car signature))
              (cdr signature))))

    ;; When the music function definition contains an i10n doc string,
    ;; (_i "doc string"), keep the literal string only
    `(ly:make-music-function
      (list ,@(map (lambda (pred)
                     (if (pair? pred)
                         `(cons ,(car pred)
                                ,(and (pair? (cdr pred)) (cadr pred)))
                         pred))
                   (cons type signature)))
      ,(currying-lambda args docstring (if docstring (cdr body) body)))))

#(defmacro-public define-my-music-function rest
  `(define-my-syntax-function
    ,(car rest)
    (ly:music? (make-music 'Music 'void #t))
    ,@(cdr rest)))

tst =
#(define-my-music-function 5 (xy mus)
  (((lambda (x) (not (ly:music? x))) #f)
   ly:music?)
  "DOCME"
  (format #t "\n\tTHIS IS EXPERIMANTAL CODE. DON'T USE IT FOR SERIOUS WORK\n\n")
  (pretty-print (list xy-1 xy-2 xy-3 xy-4 xy-5))
  (if (number-pair? xy-2)
      #{ \once \override Rest.extra-offset = #xy-2 $mus #}
      #{ #}))

{ \tst r4 r2 r4 }
{ \tst #'a r4 r2 r4 }
{ \tst #'a #(cons 1 2) r4 r2 r4 }
{ \tst #'a #(cons 1 2) #'(cons 3 4) r4 r2 r4 }
{ \tst #'a #(cons 1 2) #'(cons 3 4) #'(some useful input?) r4 r2 r4 }
{ \tst #'a #(cons 1 2) #'(cons 3 4) #'(some useful input?) #"foo" r4 r2 r4 }


Cheers,
  Harm


reply via email to

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