X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=Documentation%2Fextending%2Fscheme-tutorial.itely;h=5984c4f038c926d962627ef67aafb5b6cf1e886f;hb=77267b700c377fd170abcbf4863728937038eb5e;hp=b73c2bb9fd7ad7b91e0c193d98257dfc71af6bac;hpb=d409c67cbbdebe840220c17f796544a8a9dd193e;p=lilypond.git diff --git a/Documentation/extending/scheme-tutorial.itely b/Documentation/extending/scheme-tutorial.itely index b73c2bb9fd..5984c4f038 100644 --- a/Documentation/extending/scheme-tutorial.itely +++ b/Documentation/extending/scheme-tutorial.itely @@ -8,7 +8,7 @@ Guide, node Updating translation committishes.. @end ignore -@c \version "2.17.6" +@c \version "2.19.22" @node Scheme tutorial @chapter Scheme tutorial @@ -72,7 +72,7 @@ see @rlearning{Other sources of information}. Alternatively, Windows users may simply choose @q{Run} from the Start menu and enter @q{guile}. -However, a hands-on Scheme sandbox with all of Lilypond loaded is +However, a hands-on Scheme sandbox with all of LilyPond loaded is available with this command line: @example lilypond scheme-sandbox @@ -258,7 +258,7 @@ Scheme procedures @code{car} and @code{cdr}, respectively. @lisp guile> (define mypair (cons 123 "hello there") -... ) +@dots{} ) guile> (car mypair) 123 guile> (cdr mypair) @@ -275,9 +275,10 @@ Abelson, see @node Lists @unnumberedsubsubsec Lists -A very common Scheme data structure is the @emph{list}. Formally, a -list is defined as either the empty list (represented as @code{'()}, -or a pair whose @code{cdr} is a list. +A very common Scheme data structure is the @emph{list}. Formally, +a @q{proper} list is defined to be either the empty list with its +input form @code{'()} and length@tie{}0, or a pair whose +@code{cdr} in turn is a shorter list. There are many ways of creating lists. Perhaps the most common is with the @code{list} procedure: @@ -287,12 +288,19 @@ guile> (list 1 2 3 "abc" 17.5) (1 2 3 "abc" 17.5) @end lisp -As can be seen, a list is displayed in the form of individual elements -separated by whitespace and enclosed in parentheses. Unlike a pair, -there is no period between the elements. +Representing a list as individual +elements separated by whitespace and enclosed in parentheses +is actually a compacted rendition of the actual dotted pairs +constituting the list, where the dot and an immediately following +starting paren are removed along with the matching closing paren. +Without this compaction, the output would have been +@lisp +(1 . (2 . (3 . ("abc" . (17.5 . ()))))) +@end lisp -A list can also be entered as a literal list by enclosing its -elements in parentheses, and adding a quote: +As with the output, a list can also be entered (after adding a +quote to avoid interpretation as a function call) as a literal +list by enclosing its elements in parentheses: @lisp guile> '(17 23 "foo" "bar" "bazzle") @@ -574,7 +582,7 @@ statement in the let block: @lisp guile> (let ((x 2) (y 3) (z 4)) (display (+ x y)) (display (- z 4)) -... (+ (* x y) (/ z x))) +@dots{} (+ (* x y) (/ z x))) 508 @end lisp @@ -615,7 +623,7 @@ Another conditional procedure in scheme is @code{cond}: @example (cond (test-expression-1 result-expression-sequence-1) (test-expression-2 result-expression-sequence-2) - ... + @dots{} (test-expression-n result-expression-sequence-n)) @end example @@ -679,7 +687,7 @@ feasible. Another way to call the Scheme interpreter from LilyPond is the use of dollar@tie{}@code{$} instead of a hash mark for introducing Scheme -expressions. In this case, Lilypond evaluates the code right after the +expressions. In this case, LilyPond evaluates the code right after the lexer has read it. It checks the resulting type of the Scheme expression and then picks a token type (one of several @code{xxx_IDENTIFIER} in the syntax) for it. It creates a @emph{copy} @@ -687,18 +695,18 @@ of the value and uses that for the value of the token. If the value of the expression is void (Guile's value of @code{*unspecified*}), nothing at all is passed to the parser. -This is, in fact, exactly the same mechanism that Lilypond employs when +This is, in fact, exactly the same mechanism that LilyPond employs when you call any variable or music function by name, as @code{\name}, with -the only difference that the name is determined by the Lilypond lexer +the only difference that the name is determined by the LilyPond lexer without consulting the Scheme reader, and thus only variable names -consistent with the current Lilypond mode are accepted. - -The immediate action of @code{$} can lead to surprises, @ref{Input -variables and Scheme}. Using @code{#} where the parser supports it -is usually preferable. Inside of music expressions, expressions -created using @code{#} @emph{are} interpreted as music. However, -they are @emph{not} copied before use. If they are part of some -structure that might still get used, you may need to use +consistent with the current LilyPond mode are accepted. + +The immediate action of @code{$} can lead to surprises, see +@ref{Importing Scheme in LilyPond}. Using @code{#} where the +parser supports it is usually preferable. Inside of music expressions, +expressions created using @code{#} @emph{are} interpreted as +music. However, they are @emph{not} copied before use. If they are +part of some structure that might still get used, you may need to use @code{ly:music-deep-copy} explicitly. @funindex $@@ @@ -730,11 +738,11 @@ as follows: @end example For the rest of this section, we will assume that the data is entered -in a music file, so we add@tie{}@code{#}s at the beginning of each Scheme +in a music file, so we add a @code{#} at the beginning of each Scheme expression. All of the top-level Scheme expressions in a LilyPond input file can -be combined into a single Scheme expression by the use of the +be combined into a single Scheme expression by use of the @code{begin} statement: @example @@ -769,17 +777,28 @@ twentyFour = #(* 2 twelve) @end example @noindent -which would result in the number 24 being stored in the -LilyPond (and Scheme) variable @code{twentyFour}. - -The usual way to refer to Lilypond variables, @ref{LilyPond Scheme -syntax}, is to call them using a backslash, i.e., @code{\twentyFour}. -Since this creates a copy of the value for most of LilyPond's internal -types, in particular music expressions, music functions don't usually -create copies of material they change. For this reason, music -expressions given with @code{#} should usually not contain material that -is not either created from scratch or explicitly copied rather than -directly referenced. +which would result in the number @emph{24} being stored in the LilyPond +(and Scheme) variable @code{twentyFour}. + +Scheme allows modifying complex expressions in-place and LilyPond makes +use of this @q{in-place modification} when using music functions. But +when music expressions are stored in variables rather than entered +directly the usual expectation, when passing them to music functions, +would be that the original value is unmodified. So when referencing a +music variable with leading backslash (such as @code{\twentyFour}), +LilyPond creates a copy of that variable's music value for use in the +surrounding music expression rather than using the variable's value +directly. + +Therefore, Scheme music expressions written with the @code{#} syntax +should be used for material that is created @q{from scratch} (or that is +explicitly copied) rather than being used, instead, to directly +reference material. + +@seealso +Extending: +@ref{LilyPond Scheme syntax}. + @node Input variables and Scheme @subsection Input variables and Scheme @@ -820,7 +839,7 @@ traLaLa = @{ c'4 d'4 @} is internally converted to a Scheme definition: @example -(define traLaLa @var{Scheme value of `@code{... }'}) +(define traLaLa @var{Scheme value of `@code{@dots{}}'}) @end example This means that LilyPond variables and Scheme variables may be freely @@ -862,14 +881,14 @@ Instead of defining @code{\twice}, the example above could also have been written as @example -... +@dots{} $(make-sequential-music newLa) @end example You can use @code{$} with a Scheme expression anywhere you could use @code{\@var{name}} after having assigned the Scheme expression to a variable @var{name}. This replacement happens in the @q{lexer}, so -Lilypond is not even aware of the difference. +LilyPond is not even aware of the difference. One drawback, however, is that of timing. If we had been using @code{$} instead of @code{#} for defining @code{newLa} in the above example, the @@ -885,7 +904,7 @@ context. Using those, the last part of the example could have been written as @example -... +@dots{} @{ #@@newLa @} @end example @@ -905,7 +924,7 @@ If you need it to be executed at a later point of time, check out #(define (nopc) (ly:set-option 'point-and-click #f)) -... +@dots{} #(nopc) @{ c'4 @} @end example @@ -1104,7 +1123,7 @@ to create complicated music functions. When writing a music function it is often instructive to inspect how a music expression is stored internally. This can be done with the -music function @code{\displayMusic} +music function @code{\displayMusic}. @example @{ @@ -1127,51 +1146,58 @@ will display 'text "f")) 'duration - (ly:make-duration 2 0 1 1) + (ly:make-duration 2 0 1/1) 'pitch (ly:make-pitch 0 0 0)))) @end example By default, LilyPond will print these messages to the console along with all the other messages. To split up these messages and save -the results of @code{\display@{STUFF@}}, redirect the output to -a file. +the results of @code{\display@{STUFF@}}, you can specify an optional +output port to use: @example -lilypond file.ly >display.txt +@{ + \displayMusic #(open-output-file "display.txt") @{ c'4\f @} +@} @end example -With a combined bit of Lilypond and Scheme magic, you can actually -let Lilypond direct just this output to a file of its own: - +This will overwrite a previous output file whenever it is called; if you +need to write more than one expression, you would use a variable for +your port and reuse it: @example @{ - #(with-output-to-file "display.txt" - (lambda () #@{ \displayMusic @{ c'4\f @} #@})) + port = #(open-output-file "display.txt") + \displayMusic \port @{ c'4\f @} + \displayMusic \port @{ d'4 @} + #(close-output-port port) @} @end example +Guile's manual describes ports in detail. Closing the port is actually +only necessary if you need to read the file before LilyPond finishes; in +the first example, we did not bother to do so. A bit of reformatting makes the above information easier to read: @example (make-music 'SequentialMusic 'elements (list - (make-music 'NoteEvent + (make-music 'NoteEvent 'articulations (list - (make-music 'AbsoluteDynamicEvent - 'text - "f")) - 'duration (ly:make-duration 2 0 1 1) - 'pitch (ly:make-pitch 0 0 0)))) + (make-music 'AbsoluteDynamicEvent + 'text + "f")) + 'duration (ly:make-duration 2 0 1/1) + 'pitch (ly:make-pitch 0 0 0)))) @end example -A @code{@{ ... @}} music sequence has the name @code{SequentialMusic}, -and its inner expressions are stored as a list in its @code{'elements} -property. A note is represented as a @code{NoteEvent} object (storing -the duration and pitch properties) with attached information (in this -case, an @code{AbsoluteDynamicEvent} with a @code{"f"} text property) -stored in its @code{articulations} property. +A @code{@{ @dots{} @}} music sequence has the name +@code{SequentialMusic}, and its inner expressions are stored as a list +in its @code{'elements} property. A note is represented as a +@code{NoteEvent} object (storing the duration and pitch properties) with +attached information (in this case, an @code{AbsoluteDynamicEvent} with +a @code{"f"} text property) stored in its @code{articulations} property. @funindex{\void} @code{\displayMusic} returns the music it displays, so it will get @@ -1181,9 +1207,11 @@ interpreted as well as displayed. To avoid interpretation, write @node Music properties @subsection Music properties +@ignore TODO -- make sure we delineate between @emph{music} properties, @emph{context} properties, and @emph{layout} properties. These are potentially confusing. +@end ignore Let's look at an example: @@ -1194,7 +1222,7 @@ someNote = c' (make-music 'NoteEvent 'duration - (ly:make-duration 2 0 1 1) + (ly:make-duration 2 0 1/1) 'pitch (ly:make-pitch 0 0 0)) @end example @@ -1212,7 +1240,7 @@ someNote = (list (make-music 'NoteEvent 'duration - (ly:make-duration 2 0 1 1) + (ly:make-duration 2 0 1/1) 'pitch (ly:make-pitch 0 0 0)))) @end example @@ -1230,13 +1258,13 @@ expression. (make-music 'NoteEvent 'duration - (ly:make-duration 2 0 1 1) + (ly:make-duration 2 0 1/1) 'pitch (ly:make-pitch 0 0 0)) @end example Then the note pitch is accessed through the @code{'pitch} property -of the @code{NoteEvent} object, +of the @code{NoteEvent} object. @example #(display-scheme-music @@ -1246,7 +1274,7 @@ of the @code{NoteEvent} object, (ly:make-pitch 0 0 0) @end example -The note pitch can be changed by setting this @code{'pitch} property, +The note pitch can be changed by setting this @code{'pitch} property. @funindex \displayLilyMusic @@ -1256,7 +1284,7 @@ The note pitch can be changed by setting this @code{'pitch} property, (ly:make-pitch 0 1 0)) ;; set the pitch to d'. \displayLilyMusic \someNote ===> -d' +d'4 @end example @@ -1281,7 +1309,7 @@ representation of the desired result. 'span-direction -1)) 'duration - (ly:make-duration 2 0 1 1) + (ly:make-duration 2 0 1/1) 'pitch (ly:make-pitch 0 5 0)) (make-music @@ -1292,7 +1320,7 @@ representation of the desired result. 'span-direction 1)) 'duration - (ly:make-duration 2 0 1 1) + (ly:make-duration 2 0 1/1) 'pitch (ly:make-pitch 0 5 0)))) @end example @@ -1301,7 +1329,7 @@ The bad news is that the @code{SlurEvent} expressions must be added @q{inside} the note (in its @code{articulations} property). -Now we examine the input, +Now we examine the input. @example \displayMusic a' @@ -1309,7 +1337,7 @@ Now we examine the input, (make-music 'NoteEvent 'duration - (ly:make-duration 2 0 1 1) + (ly:make-duration 2 0 1/1) 'pitch (ly:make-pitch 0 5 0)))) @end example @@ -1317,13 +1345,13 @@ Now we examine the input, So in our function, we need to clone this expression (so that we have two notes to build the sequence), add a @code{SlurEvent} to the @code{'articulations} property of each one, and finally make a -@code{SequentialMusic} with the two @code{EventChords}. For adding to a +@code{SequentialMusic} with the two @code{NoteEvent} elements. For adding to a property, it is useful to know that an unset property is read out as @code{'()}, the empty list, so no special checks are required before we put another element at the front of the @code{articulations} property. @example -doubleSlur = #(define-music-function (parser location note) (ly:music?) +doubleSlur = #(define-music-function (note) (ly:music?) "Return: @{ note ( note ) @}. `note' is supposed to be a single note." (let ((note2 (ly:music-deep-copy note))) @@ -1348,7 +1376,7 @@ function to add an articulation (like a fingering instruction) to a single note inside of a chord which is not possible if we just merge independent music. -A @code{$variable} inside the @code{#@{...#@}} notation is like +A @code{$variable} inside the @code{#@{@dots{}#@}} notation is like a regular @code{\variable} in classical LilyPond notation. We know that @@ -1366,7 +1394,7 @@ the articulation to an empty chord, @noindent but for the sake of this example, we will learn how to do this in -Scheme. We begin by examining our input and desired output, +Scheme. We begin by examining our input and desired output. @example % input @@ -1375,7 +1403,7 @@ Scheme. We begin by examining our input and desired output, (make-music 'NoteEvent 'duration - (ly:make-duration 2 0 1 1) + (ly:make-duration 2 0 1/1) 'pitch (ly:make-pitch -1 0 0)))) ===== @@ -1390,7 +1418,7 @@ Scheme. We begin by examining our input and desired output, 'articulation-type "accent")) 'duration - (ly:make-duration 2 0 1 1) + (ly:make-duration 2 0 1/1) 'pitch (ly:make-pitch -1 0 0)) @end example @@ -1420,7 +1448,7 @@ from its name. (this is good practice in other programming languages, too!) @example -"Add an accent..." +"Add an accent@dots{}" @end example @noindent @@ -1494,12 +1522,12 @@ Finally, once we have added the accent articulation to its @code{articulations} property, we can return @code{note-event}, hence the last line of the function. -Now we transform the @code{add-accent} function into a music -function (a matter of some syntactic sugar and a declaration of the type -of its sole @q{real} argument). +Now we transform the @code{add-accent} function into a music function (a +matter of some syntactic sugar and a declaration of the type of its +argument). @example -addAccent = #(define-music-function (parser location note-event) +addAccent = #(define-music-function (note-event) (ly:music?) "Add an accent ArticulationEvent to the articulations of `note-event', which is supposed to be a NoteEvent expression." @@ -1510,7 +1538,7 @@ addAccent = #(define-music-function (parser location note-event) note-event) @end example -We may verify that this music function works correctly, +We then verify that this music function works correctly: @example \displayMusic \addAccent c4 @@ -1544,13 +1572,13 @@ TODO Find a simple example @ignore @lilypond[quote,verbatim,ragged-right] -padText = #(define-music-function (parser location padding) (number?) +padText = #(define-music-function (padding) (number?) #{ \once \override TextScript.padding = #padding #}) -\relative c''' { - c4^"piu mosso" b a b +\relative { + c'''4^"piu mosso" b a b \padText #1.8 c4^"piu mosso" d e f \padText #2.6 @@ -1567,16 +1595,16 @@ We can use it to create new commands: @lilypond[quote,verbatim,ragged-right] -tempoPadded = #(define-music-function (parser location padding tempotext) +tempoPadded = #(define-music-function (padding tempotext) (number? markup?) #{ \once \override Score.MetronomeMark.padding = #padding \tempo \markup { \bold #tempotext } #}) -\relative c'' { +\relative { \tempo \markup { "Low tempo" } - c4 d e f g1 + c''4 d e f g1 \tempoPadded #4.0 "High tempo" g4 f e d c1 } @@ -1586,7 +1614,7 @@ tempoPadded = #(define-music-function (parser location padding tempotext) Even music expressions can be passed in: @lilypond[quote,verbatim,ragged-right] -pattern = #(define-music-function (parser location x y) (ly:music? ly:music?) +pattern = #(define-music-function (x y) (ly:music? ly:music?) #{ #x e8 a b #y b a e #})