“Defining a Music Function”

It’s been about a year since I started a category with Scheme tutorials, and back then I declared them as a “documentation of my own thorny learning path”. By now I’ve experienced a significant boost in Scheme “fluency” which was triggered by (and at the same time enabled) a number of projects and enhancements, for example the ScholarLY package and the jump into a fundamental redesign of openLilyLib. I thought it would be a good idea to pick up the tradition of these tutorials before I forget too much about the difficulties of finding my way around LilyPond’s Scheme. This is of course not a carefully crafted “curriculum” but it will always be a random collection of (hopefully) useful snippets of information, each one written with the goal of explaining a single topic in more depth and at the same time more casually than the LilyPond reference can do.

Today I’m writing a tutorial that I would have needed a year ago 😉 about one thing that always vaguely confused me. I usually managed to just get around it by either routinely “doing it as always” or by getting some ready-to-use code snippets from a friendly soul on lilypond-user. This is the topic of defining music-/scheme- and void-functions in Scheme. I will analyze a music function I introduced in last years’ posts and explain what is going on there. Understanding this gave me surprising insights, and I think knowing this kind of stuff is really helpful when trying to get more familiar with using Scheme in LilyPond.

So this is the simple music function we’re going to dissect, it takes a color? argument and applies that color to the next note’s note head and stem:

colorNote =
#(define-music-function (parser location my-color)
   (color?)
   #{
     \once \override NoteHead.color = #my-color
     \once \override Stem.color = #my-color
   #})

{
  c' \colorNote #red d' c'
}

(See the other post for the output of that example.)

This is something I knew how to put together for quite some time, but I’ve always wondered about a few things here (just managed to suppress the questions, because it did work), particularly the relation between the colorNote = and the #(define-music-function part and the parser location arguments. But to be honest, I wasn’t really clear about the part returning the “music expression” either.

In the current post I will go into quite some detail about the declaration/definition of the music function and the topic of “return values”. However, I’ll skip the third issue because that’s somewhat unrelated to the other two and because the post is already quite long without it.

Defining the Music Function

Let’s start with looking at it from the perspective of the “user” or “caller”. colorNote is a “music function” that returns a “music expression”. This is the part enclosed in the #{ #} brackets, containing two overrides (yes, overrides are also “music”) and applying the #my-color argument passed into the function. So when writing \colorNote #red it’s quite obvious that I call the function colorNote, passing it the argument #red.

But the syntax of how this “function” is defined somehow always startled me, and I’m sure there are many others who could write such a function too, without really knowing what they are doing. Let’s have a look at a comparable function definition in Python (for those who know Python):

def colorNote(parser location color):
    return some_music_expression

Here the syntax is clear that we are defining colorNote to be a function object, taking some arguments and returning a value. When we use that function later in the code the program execution will jump right into the body of that function definition. But what do we actually do when “defining a music function” in LilyPond?

From the LilyPond documentation (and last year’s posts) we learn that the following expressions are equivalent:

myVariable = 5

#(define myVariable 5)

Both define a variable myVariable and set its value to the integer number 5. Or, expressed the other way round, they take the value of 5 and bind it to the name myVariable. Later in the program (or the LilyPond file) one can refer to this name and get the value back.

We can rewrite the definition using the #(define syntax like this:

#(define colorNote
   (define-music-function (parser location my-color)
     (color?)
     ; ...

So what is the value we are binding to the name colorNote in our example?

Intuitively I would expect that we bind a function’s implementation to the name colorNote, similar to what the Python example seems to do. But here we don’t seem to assign a function or function body but define-music-function instead. If you start thinking about it this seems very strange. Fortunately you can continue thinking about it and it becomes clear, so stay tuned…

Maybe you notice a small but important difference to the above definition of myVariable: define-music-function is enclosed in parentheses, while 5 was not. Parens (usually) indicate we are calling a procedure in Scheme, and this call evaluates to something. Whenever you want to use a value you can instead call a procedure, and the value this procedure evaluates to is then used as your value. (You may want to read this paragraph again… or consider the following mathematical examples. In Scheme (+ 3 2) evaluates to 5, (- 3 2) evaluates to 1, and (+ 3 (+ 1 1)) evaluates to (+ 3 2) which then evaluates to 5.)

So what we really do with our music function is call define-music-function which evaluates to a “music function” and bind this result to the name colorNote. Later in the LilyPond file when we call \colorNote we do not execute the code after \colorNote = (which is what would happen in the Python example) but instead we call the music function that has been created when \colorNote has been initially parsed. (For a more detailed and technical discussion you may read the Wikipedia article about “first class functions”).

define-music-function <argument-list> <argument-predicates> <body> itself takes three arguments, each enclosed in its own parenthesis (here the parens are used for grouping items to a list and not for a procedure call):

  • the list of argument names:
    (parser location my-color)
  • a list of argument predicates (types that the arguments have to have)
    (color?)
  • the actual implementation body

my-color is an arbitrary name that has been chosen for an argument. It lets you access the value that has been passed into the music function at that position. Note that this is the only argument that the user has to supply when calling the music function, parser and location are passed implicitly. According to the manual parser location simply has to be copied literally, which is also confusing – but we won’t go into this detail today.

color? is the type of the (single) value that can be passed to the function, so you can’t for example write \colorNote #'thisIsNotAColor (which would pass a Scheme symbol to the function).

Side note: You also can define music functions that don’t have such arguments, so the first element in define-music-function would be (parser location). It has always startled me why I’d have to add () in such cases, but now this becomes clear: define-music-function expects a list of argument predicates as its second argument, and if there are no arguments to be type-checked then this second argument is still expected, and an empty list has to be passed as the <argument-predicates>.

The “Return Value” – Music-, Scheme- and Void Functions

Digression: “Procedures” and “Functions”

Before going into the topic of the different function types I have to dwell on a certain fuzziness in terminology: procedures and functions. When reviewing this post I realized that I wasn’t completely clear about the distinction and used them interchangeably. My request on the lilypond-user mailing list raised a discussion showing that it actually isn’t a trivial topic. So while in the end it is more or less neglectable there are things you may want to digest in order not to get confused when people use these terms in the LilyPond/Scheme context.

Some programming languages make a distinction between procedures and functions, some don’t. If a language distinguishes, it is mostly the question of a return value: functions return a value, procedures don’t. This means that while both are separate blocks of code that can be called from within a program, functions produce a value that can be worked with while procedures just “do” something which doesn’t directly affect the calling code.

Other languages don’t make a distinction and call both types procedures or functions and usually have a syntactic way to clarify the behaviour. However, it’s quite common that people distinguish although their programming language doesn’t. If you notice this just try to ignore that and don’t be confused.

The implementation of the Scheme programming language that is used by LilyPond is Guile 1.8. In this basically everything is considered a procedure, regardless of having a return value or not. Take the following expression:

(car '(1 2 3 4 5))

This expression is a procedure call, namely the call to the procedure car. The list '(1 2 3 4 5) is passed as the argument to car, which evaluates to 1, the first element of the list. So the “return value” that is then used in the program is 1. Other procedures, for example (display '(1 2 3 4 5)) do not evaluate to anything, so the “value” in the place of the procedure call is <unspecified>.

Both are called “procedure” in Guile’s terminology although one returns a value and the other does not. However, you will often encounter the naming convention of calling the “returning” versions “function”. This is actually against the official naming convention of the Scheme dialect that LilyPond uses, but it is quite common and doesn’t pose a real-world problem. And – as far as I can see – this is also true for the terms “music function”, “scheme function” and “void function”.


OK, let’s get back on track and consider the “return value” of our music function. Above I wrote that colorNote returns a music expression containing two overrides. But what does that actually mean?

The body of a procedure in Scheme is a sequence of expressions, and each expression can be either a value or a procedure call. The value of the last expression in the body is the value the whole function evaluates to – or, more colloquially, is the return value of the function. In the case of \colorNote this last expression is not a Scheme expression but a LilyPond music expression, as indicated by the #{ #}. From Scheme’s perspective this is a single value (of type ly:music?), but from LilyPond’s (or rather a user’s) perspective this music expression can also be a grouped sequence of music elements – in our example we have two consecutive overrides.

To conclude we can say that a “music function” is a procedure whose last expression evaluates to LilyPond-music. It can be called everywhere that you can write a music expression in LilyPond – just like in our initial example at the top of this post.

Now, what are scheme- and void-functions then?

The whole subject of defining these functions/procedures is identical to the definition and calling of music functions, the only (but crucial) difference is the return value. A procedure defined using define-scheme-function can return any valid Scheme value, and it can be used anywhere the respective Scheme value can be used. The following example takes a string as its argument and returns sort of a palindrome version (just for the sake of the example). The type of the return value is string?, and this can for example be used to set a header field.

addPalindrome =
#(define-scheme-function (parser location my-string)
     (string?)
     (ly:message "We will add the reverse of the string to itself")
     (string-append my-string (string-reverse my-string))
     )

\header {
  title = \addPalindrome "OT"
}

{
  c' 
}

The “body” of this procedure is a sequence of two expressions. The first one (ly:message prints something to the console output but doesn’t evaluate to a value, the second is the call to string-append, which is a procedure call that evaluates to a string.

Side note 1: Here again you can see an example of nested procedure calls and their evaluations: string-append here takes two arguments, the first being a value (namely the argument my-string), while the second argument is again a procedure call. The operations that Scheme actually performs one after another are:

(string-append my-string (string-reverse my-string))
(string-append my-string (string-reverse "OT"))
(string-append my-string "TO")
(string-append "OT" "TO")
"OTTO"

So the nested expression in the first line of this example eventually evaluates to “OTTO”. And as this is the last expression in the procedure body its value will be the return value of the procedure as a whole, which in this example is used as the title of the score.

Side note 2: You can see that there is a single closing parenthesis on the last line of the procedure. This matches the opening paren in #(define-scheme-function. Scheme’s coding guidelines suggest not to place parens on their own lines but rather concatenate them at the end of previous lines. As you can already see in these simple examples nesting procedure calls can quickly build up, so it’s not uncommon to encounter Scheme procedures with, say, ten closing parens in the last line. However, I laid it out like this to explicitly show that each line in the example is one expression. Temporarily reformatting is a very useful tool for debugging procedures or to understand the structure of existing procedures you are looking at. Don’t hesitate to insert line breaks and make use of your editor’s assistance to re-indent the code as this will make things much clearer. Once everything is ready it’s advisable to re-compress the procedure again, even if you are used to other layouts that are common in other programming languages.

Probably you can by now guess what a void-function is – basically the same as the other two, but without a return value. So you will want to use define-void-function when you want the procedure to actually do something (also known as “side effects”) but don’t need any return value. The following example will print out a message to the console:

displayLocation =
#(define-void-function (parser location)
     ()
     (ly:input-message location "This was called from a 'void-function'")
     )
     
{
  c'
  \displayLocation
}

There is just one expression in the function body, printing a message. In the case of define-void-function it doesn’t matter if this (respectively the last) expression evaluates to something or not, the function won’t return any value at all. This also has the effect that you can actually call void functions from anywhere. The parser won’t try to use them as a value but will simply execute its code. So the following example is equally valid and working.

displayLocation =
#(define-void-function (parser location)()
   (ly:input-message location "This was called from a 'void-function'"))

\displayLocation

I hope this post helped you understanding a few basic things about how music, scheme, and void functions work and how they are integrated in LilyPond documents. This is only a tiny start, but understanding these concepts thoroughly definitely helps with proceeding to more complex and more powerful functions. As a final “assignment” I’ll leave it to you to figure out what the location does in the last example, how it is used and how its value actually got into the function.

10 thoughts on ““Defining a Music Function”

  1. Helge

    Great blog article! This is fundamental for any user who wants to start coding functions in Lilypond. I found many solutions for my questions in the mailing list, but seldom understand why this solves the problem and how it does. The post must be boring for any Schemer but is very appreciated for the frogs at the ponds coast.
    I am very curious what the parser and location arguments are for and hope you will find the time to continue on this thorny way.

    Reply
    1. Urs Liska

      Thank you, this is exactly the motivation to write this. Ready-made solutions are helpful on immediately but don’t add too much to a user’s understanding. Same with suggestions à la “Well, couldn’t you simply use an engraver for that?”.

      I would really like to get others joining this effort of providing similar in-depth discussions that are intended to get people “on the road” who are basically able to learn extending LilyPond with Scheme but who are struggling with the too concise documentation.

      Reply
  2. Peter Gentry

    Excellent stuff Urs simple enough but not too simple.

    I conclude rightly or wrongly that “parser” refers to a function that reads and deciphers the input file and “location” is the current postion of the parser.
    The value “location” contains the full input file name plus the line and column that the parser is currently “reading” when the void function is called.

    I had always wondered what parser location indictated – many (most?) of the objects in Lily Scheme are not intuitive ; quesswork can lead up any number of blind alleys.

    Reply
  3. David Kastrup

    As of issue 4422 (to appear in version 2.19.22), the parser location arguments to music functions et al are gone.

    Issue 4442 will obliterate the parser argument from most uses inside of LilyPond’s code base.

    Accessing the current parser may be necessary for specific purposes, and the function call (*parser*) is available for that now, like the analogously named (*location*) for the current source code location (the displayLocation would be using (*location*) instead of location for its call to ly:input-message now and be defined without parser location arguments). But unless you have a particular need for those mostly internal entities, it will no longer be necessary to drag them around in plain sight until there is some potential use for them. The old forms will continue working for a while.

    As of issue 4421, music functions can be called straight from within Scheme as if they were ordinary functions, so (time '(3 . 2)) will be equivalent to #{ \time 3/2 #}.

    So in other words: music functions lose some of their mysterious aspects, and it gets easier to experiment near the Scheme/LilyPond border.

    Reply
    1. Urs Liska

      Thank you , David.

      This may look like a mere syntax change, but in fact it is one more (and a significant) step on your crusade to make LilyPond’s language more consistent, transparent and accessible.

      Reply
      1. David Kastrup

        Well, it also has the advantage of making the following two music functions one and two behave equivalently:


        #(define (sub) #{ { c'2 } #})

        one =
        #(define-music-function (parser location) () (sub))

        two =
        #(define-music-function (parser location) () #{ { c'2 } #})

        { \one \two }

        Can you spot the difference? Probably not. But try clicking on either note in the PDF file (point-and-click). #{#} relies on location for its source information. So clicking on the first note, absent other useful information bound to the variable name location, moves you to some point near the # starting the definition of sub, while clicking on the second note moves you to the use of \two.

        If the function sub had been passed parser location arguments, its #{#} would have differed in behavior accordingly.

        In future, this difference will be gone since the respective information is passed implicitly to any called functions and the variable names parser and location have no relevance to #{#} any more.

        Reply
    2. Urs Liska

      Just to confirm that:

      \version "2.19.22"
      
      test =
      #(define-music-function (color)(color?)
         #{ \once \override NoteHead.color = #color #})
      
      {
        c' d' \test #green d' c'
      }

      looks just so much cleaner than having the two additional standard arguments to be present!

      Reply
      1. David Kastrup

        Well, issues 4421 and 4445 allow you to write this as

        test =
        #(define-music-function (color)(color?)
           (once (override '(NoteHead color) color)))

        Look Ma, I can write in Scheme!

        Reply
        1. David Kastrup

          And finally issue 4487 allows us to define this music function as

          test = \once \override NoteHead.color = \etc

          While this kind of abbreviation syntax is only useful for rather simple music functions, it works for things like stacking tweaks on a single music expression.

          Reply
  4. Pingback: Just Intonation – Semantic Encoding with LilyPond | Scores of Beauty

Leave a Reply

Your email address will not be published. Required fields are marked *