unofficial mirror of guile-user@gnu.org 
 help / color / mirror / Atom feed
* Macro to prepend element to list
@ 2021-03-20 14:24 Jean Abou Samra
  2021-03-20 16:04 ` Linus Björnstam
  0 siblings, 1 reply; 5+ messages in thread
From: Jean Abou Samra @ 2021-03-20 14:24 UTC (permalink / raw)
  To: guile-user

Hello,

I find myself frequently using the following macro:

(define-macro (prepend! thing lst)
    `(set! ,lst (cons ,thing ,lst)))

Have I missed a module somewhere that does this kind of things?
At least, I couldn't find anything in SRFIs. It may also be the
case that this is too specific to certain non-functional usages
(LilyPond in my case).

Thanks in advance,
Jean Abou Samra




^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Macro to prepend element to list
  2021-03-20 14:24 Macro to prepend element to list Jean Abou Samra
@ 2021-03-20 16:04 ` Linus Björnstam
  2021-03-20 17:05   ` Jean Abou Samra
  0 siblings, 1 reply; 5+ messages in thread
From: Linus Björnstam @ 2021-03-20 16:04 UTC (permalink / raw)
  To: Jean Abou Samra, guile-user

Well, mutating like that is not very common, except for maybe with alists.

In which situations are you mutating the list like that? Usually you would build a reverse list using a recursive function and an accumulator, which can be done without set! (which has a boxing overhead).

-- 
  Linus Björnstam

On Sat, 20 Mar 2021, at 15:24, Jean Abou Samra wrote:
> Hello,
> 
> I find myself frequently using the following macro:
> 
> (define-macro (prepend! thing lst)
>     `(set! ,lst (cons ,thing ,lst)))
> 
> Have I missed a module somewhere that does this kind of things?
> At least, I couldn't find anything in SRFIs. It may also be the
> case that this is too specific to certain non-functional usages
> (LilyPond in my case).
> 
> Thanks in advance,
> Jean Abou Samra
> 
> 
>



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Macro to prepend element to list
  2021-03-20 16:04 ` Linus Björnstam
@ 2021-03-20 17:05   ` Jean Abou Samra
  2021-03-20 18:10     ` Linus Björnstam
  0 siblings, 1 reply; 5+ messages in thread
From: Jean Abou Samra @ 2021-03-20 17:05 UTC (permalink / raw)
  To: Linus Björnstam, guile-user

[-- Attachment #1: Type: text/plain, Size: 1293 bytes --]

Le 20/03/2021 à 17:04, Linus Björnstam a écrit :

> Well, mutating like that is not very common, except for maybe with alists.
>
> In which situations are you mutating the list like that? Usually you would build a reverse list using a recursive function and an accumulator, which can be done without set! (which has a boxing overhead).


Mostly to mutate properties of LilyPond's probs (property objects).
This works somewhat like Guile's object properties. For example, to
add articulations on notes:

\version "2.23.1"

#(define-macro (prepend! thing lst)
    `(set! ,lst (cons ,thing ,lst)))

addStaccato =
#(define-music-function (music) (ly:music?)
    (map-some-music
      (lambda (m)
        (if (music-is-of-type? m 'note-event)
            (prepend! (make-music 'ArticulationEvent 'articulation-type 
"staccato")
                      (ly:music-property m 'articulations)))
        #f)
      music))

\addStaccato { c'4 d' e'8 f' g' a' }

(Output attached.)

It is also of use in so-called engravers (it's harder
to find a simple use case for these). I guess all of this
is so LilyPond-specific that it suits better in my personal
libraries, and maybe upstream if I see a compelling use
case in the code base.

Thanks!
Jean


[-- Attachment #2: prepend-example.ly --]
[-- Type: text/x-lilypond, Size: 435 bytes --]

\version "2.23.1"

#(define-macro (prepend! thing lst)
   `(set! ,lst (cons ,thing ,lst)))

addStaccato =
#(define-music-function (music) (ly:music?)
   (map-some-music
     (lambda (m)
       (if (music-is-of-type? m 'note-event)
           (prepend! (make-music 'ArticulationEvent 'articulation-type "staccato")
                     (ly:music-property m 'articulations)))
       #f)
     music))

\addStaccato { c'4 d' e'8 f' g' a' }

[-- Attachment #3: prepend-example.pdf --]
[-- Type: application/pdf, Size: 27950 bytes --]

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Macro to prepend element to list
  2021-03-20 17:05   ` Jean Abou Samra
@ 2021-03-20 18:10     ` Linus Björnstam
  2021-03-20 20:16       ` Jean Abou Samra
  0 siblings, 1 reply; 5+ messages in thread
From: Linus Björnstam @ 2021-03-20 18:10 UTC (permalink / raw)
  To: Jean Abou Samra, guile-user

I see!

To be honest, this seems like a guile-1.8ism... I don't think such code would work for guile2 though 3. Why? Because your are mutating a pointer local to your procedure, not the actual data pointed to by the music property You are modifying the pointer to the list returned by the call, not the list where it is stored. 

I don't know enough lilypond, but a more standard way of doing that would be something like (ly:music-property-set! m 'articulations (cons  (make-music 'ArticulationEvent 'articulation-type "staccato") (ly:music-property m 'articulations))) (or a shorthand thereof).

More verbose, sure, but also standard scheme...

This could of course be abstracted away in some nicer way, like something I just pulled out of my posterior without much prior knowledge of lilypond (except for a fingering chart for bassoon I maintain): 

(update! (select '(note-event 'artuculations)) (lambda (p) (cons (make-music ...) m)))

 This could be done with higher order functions and passing lambdas around without having to rely on macros...

-- 
  Linus Björnstam

On Sat, 20 Mar 2021, at 18:05, Jean Abou Samra wrote:
> Le 20/03/2021 à 17:04, Linus Björnstam a écrit :
> 
> > Well, mutating like that is not very common, except for maybe with alists.
> >
> > In which situations are you mutating the list like that? Usually you would build a reverse list using a recursive function and an accumulator, which can be done without set! (which has a boxing overhead).
> 
> 
> Mostly to mutate properties of LilyPond's probs (property objects).
> This works somewhat like Guile's object properties. For example, to
> add articulations on notes:
> 
> \version "2.23.1"
> 
> #(define-macro (prepend! thing lst)
>     `(set! ,lst (cons ,thing ,lst)))
> 
> addStaccato =
> #(define-music-function (music) (ly:music?)
>     (map-some-music
>       (lambda (m)
>         (if (music-is-of-type? m 'note-event)
>             (prepend! (make-music 'ArticulationEvent 'articulation-type 
> "staccato")
>                       (ly:music-property m 'articulations)))
>         #f)
>       music))
> 
> \addStaccato { c'4 d' e'8 f' g' a' }
> 
> (Output attached.)
> 
> It is also of use in so-called engravers (it's harder
> to find a simple use case for these). I guess all of this
> is so LilyPond-specific that it suits better in my personal
> libraries, and maybe upstream if I see a compelling use
> case in the code base.
> 
> Thanks!
> Jean
> 
> 
> Attachments:
> * prepend-example.ly
> * prepend-example.pdf



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Macro to prepend element to list
  2021-03-20 18:10     ` Linus Björnstam
@ 2021-03-20 20:16       ` Jean Abou Samra
  0 siblings, 0 replies; 5+ messages in thread
From: Jean Abou Samra @ 2021-03-20 20:16 UTC (permalink / raw)
  To: Linus Björnstam, guile-user

Le 20/03/2021 à 19:10, Linus Björnstam a écrit :

> I see!
>
> To be honest, this seems like a guile-1.8ism... I don't think such code would work for guile2 though 3. Why? Because your are mutating a pointer local to your procedure, not the actual data pointed to by the music property You are modifying the pointer to the list returned by the call, not the list where it is stored.
>
> I don't know enough lilypond, but a more standard way of doing that would be something like (ly:music-property-set! m 'articulations (cons  (make-music 'ArticulationEvent 'articulation-type "staccato") (ly:music-property m 'articulations))) (or a shorthand thereof).
>
> More verbose, sure, but also standard scheme...

Well, not sure what you mean exactly, but ly:music-property is defined 
with a setter:

(define-public ly:music-property
   (make-procedure-with-setter ly:music-property
                               ly:music-set-property!))

which makes the code equivalent to (ly:music-set-property! m 
'articulations (cons ... (ly:music-property m 'articulations))), doesn't 
it?

> This could of course be abstracted away in some nicer way, like something I just pulled out of my posterior without much prior knowledge of lilypond (except for a fingering chart for bassoon I maintain):
>
> (update! (select '(note-event 'artuculations)) (lambda (p) (cons (make-music ...) m)))
>
>   This could be done with higher order functions and passing lambdas around without having to rely on macros...

Indeed, we could do things along these lines:

\version "2.23.1"

#(define (ly:music-transform-property! music prop func)
    (ly:music-set-property! music
                            prop
                            (func (ly:music-property music prop))))

addStaccato =
#(define-music-function (music) (ly:music?)
    (map-some-music
      (lambda (m)
        (if (music-is-of-type? m 'note-event)
            (ly:music-transform-property!
              m
              'articulations
              (lambda (elts)
                (cons (make-music 'ArticulationEvent 'articulation-type 
"staccato")
                      elts))))
        #f)
      music))

\addStaccato { c'4 d' e'8 f' g' a' }

It is more verbose for prepending elements, but much more versatile, and 
'cute' from (srfi srfi-26) can make the call more compact as well.

Best regards,
Jean




^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2021-03-20 20:16 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-03-20 14:24 Macro to prepend element to list Jean Abou Samra
2021-03-20 16:04 ` Linus Björnstam
2021-03-20 17:05   ` Jean Abou Samra
2021-03-20 18:10     ` Linus Björnstam
2021-03-20 20:16       ` Jean Abou Samra

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).