set! is very unidiomatic Scheme. You could use
(let* ((one (begin (ly:grob-set-property! ...) (ly:arpeggio::print grob)))
((two ...
Yes, I'm aware that set! isn't really the essence of what functional
programming languages are meant for. But basically the whole idea of
"putting the system in one state, let it do some work, then let it do
the same work again in some other state" that this arpeggio approach
uses seemed to force me to use a quite "imperative" style of
programming. But it didn't occur to me that this could be encapsulated
in a let-begin-construction, thanks for pointing that out!
(By the way, would there be a guaranteed evaluation order for the
assignments in a let (not let*)? Of course it doesn't matter in this
case, and the double use of the combined stencil calls for a let*
anyway, but I wondered.)
So, if anyone is interested in a ready-to-use version: Getting rid of
the set!'s the way David suggested leads to the following code, which
certainly is at least more readable:
\version "2.22"
arpeggioUpDown = {
\override Arpeggio.stencil =
#(lambda (grob)
(let*
((one
(begin
(ly:grob-set-property! grob 'arpeggio-direction UP)
(ly:arpeggio::print grob)))
(two
(begin
(ly:grob-set-property! grob 'arpeggio-direction DOWN)
(ly:arpeggio::print grob)))
(one+two
(ly:stencil-add one (ly:stencil-translate-axis two 1.25 X))))
(ly:grob-set-property! grob 'X-extent
(ly:stencil-extent one+two X))
(ly:pointer-group-interface::add-grob (ly:grob-parent grob X)
'elements grob)
one+two))
\override Arpeggio.direction = #RIGHT
}
{
\once \arpeggioUpDown
<c' e' g' c''>\arpeggio
c'
}
Lukas