Hi Jean,

Thank you so much for that clear and thorough explanation. I was so caught
up in my functional transformations, that I didn't even stop to think if
mutation could be at play. I sprinkled a handful of deep-copies over my
functions and voila!

- and thanks again! I really appreciate you taking the time to help me
understand and explain these details.

Best regards,
Morten

On Thu, Feb 22, 2024 at 11:38 PM Jean Abou Samra <j...@abou-samra.fr> wrote:

> Hi Morten,
>
> Le jeudi 22 février 2024 à 23:00 +0100, Morten Lemvigh a écrit :
> > \version "2.24.1"
> >
> > double =
> > #(define-music-function (music)(ly:music?)
> >    (let ((notes (ly:music-property music 'elements)))
> >      (make-music 'SequentialMusic
> >                  'elements
> >                  (list
> >                   (make-music 'SequentialMusic
> >                               'elements notes)
> >                   (make-music 'SequentialMusic
> >                               'elements notes)))))
> >
> > \relative c' {
> >   \double {e4 e}
> > }
>
>
> Several things are wrong here.
>
> First, although you are unwrapping `music`, you are not copying the
> individual
> elements of `notes`. They end up duplicated in the output. This should
> never
> happen, because music objects are mutable. \relative is in fact a good
> example
> of this, but perhaps a simpler one is \scaleDurations: try
>
> \scaleDurations 1/2 \double { e'1 1 }
>
> and you will see that the whole notes are scaled by 1/4 instead of 1/2.
> The reason
> is that calling [note1] and [note2] the two note objects, you end up with
>
>   [note1] [note2] [note1] [note2]
>
> and when \scaleDurations goes through the music and scales it, since
> [note1]
> and [note2] both appear twice, they get scaled twice.
>
> That's the reason you should always copy music with ly:music-deep-copy
> or an equivalent if you need it to appear in several places.
>
> In your example
>
> \relative c' {
>   \double {e4 e}
> }
>
> what happens is the following: \relative changes the first e4 into an
> absolute
> e'4, to match the octave of c' . Then the second e is changed to e'4 as
> well,
> to have the same octave as the first. Then the third e is processed, but
> it's
> actually the same object as the first e4, which was mutated into e'4, so
> this
> tells \relative to go an octave higher, and that e'4 is mutated a second
> time
> into an e''4. And finally, the fourth e is processed, it's the same as the
> second
> e which was turned into e', which goes an octave up, so it becomes e'''.
> And that's why the end result is e''4 e''' e''4 e'''.
>
> So this is already a better version:
>
> double =
> #(define-music-function (music)(ly:music?)
>    (make-music 'SequentialMusic
>                'elements (list (ly:music-deep-copy music)
>                                (ly:music-deep-copy music))))
>
> or more simply put:
>
> double =
> #(define-music-function (music)(ly:music?)
>    #{ $music $music #})
>
> but in fact this is still not correct because \relative is a somewhat
> special beast. The problem is that
>
> \relative c' {
>   \double {e'4 e}
> }
>
> now unfolds to
>
> \relative c' {
>   { e'4 e e'4 e }
> }
>
> where the third e'4 goes an octave up compared to the second, but you
> don't want that, you want it to be the same as the first e'4. For that
> case, there is a special macro called make-relative. It's used like this:
>
> \version "2.24.1"
>
> double =
> #(define-music-function (music) (ly:music?)
>    (make-relative (music) music
>      #{ $music $music #}))
>
>
> Best,
> Jean
>
>

Reply via email to