Subject: Version 2.23.80 : define vs ly:parser-defined

From: Gilles Thibault <[email-masked]>
Date: Wed, 26 Oct 2022 15:30:56 +0200

Bonjour tout le monde.
Question en court :
   Peut-on employer indifféremment, pour définir une variable scheme 
simple,
       define (fournit par guile)
    et ly:parser-define! (fonction scheme fournit par Lilypond) ?

Explications :
J'avais défini dans mes fichiers de config des variables par exemple de 
la manière suivante :
#(if (not (defined? 'textSize))
      (define textSize -1))

J'ajustais tous les objets Lilypond ayant une propriété font-size en 
fonction de cette variable :
    \override TextScript.font-size = #textSize
    \override MetronomeMark.font-size = #textSize
    \override BarNumber.font-size = #(1- textSize)  % 1 point de moins
    etc...
Si pour une musique donnée, je trouvais la taille des textes encore trop 
grosse, je n'avais qu'à rajouter juste avant mon fichier config à 
inclure
   textSize = #-2
   \include "mon_fichier_config.ly"
et tous les font-size étaient ajustées en prenant en compte cette 
nouvelle valeur

La version 2 de Guile vient avec des restrictions pour la fonction 
define
   (voir le message de Jean ABOU
      
https://lists.gnu.org/archive/html/lilypond-user-fr/2022-10/msg00051.html 
)
La solution devrait être
   #(define textSize (if (defined? 'textSize)
                         textSize
                         -1))

Pour éviter de redéfinir textSize à la valeur qu'il a déjà, je voudrais 
garder la syntaxe que j'avais avant, mais en utilisant 
ly:parser-defined!

#(if (not (defined? 'textSize))
      (ly:parser-define! 'textSize -1))

... ce qui semble fonctionner.

Comme j'ai un très grand nombre de fichiers à modifier, je voudrais être 
sûr que cette solution est pérenne. Il n'y a pas de projet de 
restriction de syntaxe pour ly:parser-define! ?

-- 
Gilles

---

From: Jean Abou Samra <[email-masked]>
Date: Wed, 26 Oct 2022 16:49:49 +0200

Bonjour Gilles,

Le 26/10/2022 à 15:30, Gilles Thibault a écrit :
> J'avais défini dans mes fichiers de config des variables par exemple 
> de la manière suivante :
> Si pour une musique donnée, je trouvais la taille des textes encore 
> trop grosse, je n'avais qu'à rajouter juste avant mon fichier config à 
> inclure
>   textSize = #-2
> La version 2 de Guile vient avec des restrictions pour la fonction define

Enchanté, moi c'est Jean ABOU SAMRA :-)

> Pour éviter de redéfinir textSize à la valeur qu'il a déjà, je 
> voudrais garder la syntaxe que j'avais avant, mais en utilisant 
> ly:parser-defined!
>
> Comme j'ai un très grand nombre de fichiers à modifier, je voudrais 
> être sûr que cette solution est pérenne. Il n'y a pas de projet de 

Ton message soulève plusieurs questions.

Est-ce qu'on peut utiliser define et ly:parser-define! de
manière interchangeable ? En général, non, dans ce cas
précis, oui. define est une syntaxe prédéfinie par Guile
qui sert à créer des variables aussi bien globales que
locales. Par exemple, on peut écrire

#(define (f x)
    (define y (1+ x))
    (* 3 y))

qui est équivalent à

#(define (f x)
    (let ((y (1+ x)))
      (* 3 y)))

Tu peux aussi écrire

#(define (f x)
    (ly:parser-define! 'y (1+ x))
    (* 3 y))

mais c'est complètement différent : la variable y n'est
plus locale à la fonction, mais globale, donc visible
en dehors de la fonction, comme si tu avais fait

#(define y #f)
#(define (f x)
    (set! y (1+ x))
    (* 3 y))

En somme, au contraire de define, ly:parser-define! ne crée
jamais que des variables globales. Dans ton cas, la variable
est globale, donc les deux sont effectivement équivalents.

Ensuite, est-ce que des restrictions similaires à define risquent
d'arriver à ly:parser-define! ? Absolument pas, les deux
sont séparés. ly:parser-define! n'est pas pour autant figée
pour l'éternité, mais elle n'a pas plus de raison d'évoluer
que les autres fonctions de LilyPond, en tous cas pas à cause
de define. Les règles sur le placement de define sont purement
syntaxiques et imposées par Guile, or ly:parser-define! est
une bête fonction, pas une « syntaxe spéciale » comme define
(en termes techniques, une « macro »), donc aucun problème
de ce côté-là.

Pour finir, est-ce que je recommande personnellement d'utiliser ce
style de codage ? À vrai dire, non, pas vraiment,  pour deux raisons.
D'une part, en utilisant defined?, tu mets quand même les mains dans
le cambouis de Guile et de son interaction de bas niveau avec LilyPond.
Pourquoi pas, mais il faut savoir que defined? te réserve des surprises
si tu n'as pas étudié la manière dont ça fonctionne. Exemples :

\version "2.23.80"

#(define variable-globale 'toto)
#(display (defined? 'variable-globale)) % => #t
#(newline)

#(let ((variable-locale 'toto))
    (display (defined? 'variable-locale)) ; => #f
    (newline))

\layout {
   variable-layout = #'toto
}

\score {
   { c' }
   \layout {
     #(display (defined? 'variable-layout)) % => #t
     #(newline)
   }
}

La deuxième raison est qu'avec ce système, le réglage est global.
Tu ne peux pas inclure ton fichier de config et régler une taille
plus petite pour une partition seulement à l'intérieur d'un \book,
ou un seul \book parmi plusieurs, etc.

Somme toute, je ferais plutôt :

\version "2.23.80"

setTextSize =
#(define-music-function (textSize) (number?)
    #{
      \override TextScript.font-size = #textSize
      \override Score.MetronomeMark.font-size = #textSize
      \override Score.BarNumber.font-size = #(- textSize 1)
    #})

\layout {
   \setTextSize -1
}

En incluant cela, tu as une taille par défaut, et si tu veux la changer,
il suffit de faire

\layout {
   \setTextSize -2
}

ou bien

\book {
   \score { ... }
   \score {
     ...
     \layout {
       \setTextSize -2
     }
   }
}

Bien sûr, la transition est plus compliquée que de changer un
define en ly:parser-define!, donc si tu as vraiment beaucoup
de fichiers, pourquoi pas rester à ly:parser-define! .

Cordialement,
Jean

---

From: Gilles Thibault <[email-masked]>
Date: Thu, 27 Oct 2022 00:11:39 +0200

> Enchanté, moi c'est Jean ABOU SAMRA :-)

Hum, je me suis aperçu de mon erreur juste après mon envoi. Trop tard, 
désolé : Jean !!!

> précis, oui.
> define est une syntaxe prédéfinie par Guile
> locales.

Oui, oui j'aurai du préciser ! Pour moi la question était uniquement 
pour des variables uniquement globales

> jamais que des variables globales. <...> ly:parser-define! est

Ok, j'avais bien compris pour la portée de ly:parser-define! mais je ne 
savais pas que define était une macro
   (macro? define)     => #t
   (procedure? define) => #f

> le cambouis de Guile et de son interaction de bas niveau avec LilyPond.

   Hum, pas pratique toutes ces fonctions de base dont on doit se méfier.
   Un développeur avait aussi un jour mis des réserves sur la fonction 
primitive-eval.
   Il faudrait des fonctions sûres, estampiées Lilypond pour remplacer 
defined? et primitive-eval.
   Pour primitive-eval, ce même développeur avait proposé : (module-ref 
(current-module) sym)
   Pour defined?, il pourrait y avoir : (module-variable (current-module) 
sym)
   ... même si je ne vois pas en quoi ces fonctions "module" seraient 
plus sûrs.

> <..> il faut savoir que defined? te réserve des surprises
> si tu n'as pas étudié la manière dont ça fonctionne.

Bon dans le cas de variables non globales, effectivement il faut se 
méfier. Merci pour tes exemples.

> ou un seul \book parmi plusieurs, etc.

Pas faux, sauf que pour le cas de taille de font-size, il faut quand 
même mieux esthétiquement garder ses mêmes réglages dans tous les scores

>   \setTextSize -1

Hum, je n'aurais jamais oser mettre une fonction musicale dans un 
\layout !
Ça m'ouvre effectivement des opportunités.

> de fichiers, pourquoi pas rester à ly:parser-define! .

Oui, c'est ce que je vais faire pour l'instant.
Merci encore une fois Jean pour tes réponses longues et détaillées.

À bientôt.

-- 
Gilles

---

From: Jean Abou Samra <[email-masked]>
Date: Thu, 27 Oct 2022 00:40:03 +0200

Le 27/10/2022 à 00:11, Gilles Thibault a écrit :
> Ok, j'avais bien compris pour la portée de ly:parser-define! mais je 
> ne savais pas que define était une macro
>   (macro? define)     => #t

... en Guile 1. En Guile 2, c'est aussi une macro,
par contre cette manière de le vérifier ne marche
plus, justement parce que c'est une macro (à cause
de l'ajout de la fonctionnalité « identifier syntax »).

$ ~/lilies/2.23.80/bin/lilypond scheme-sandbox
GNU LilyPond 2.23.80 (running Guile 2.2)
Traitement de 
« /home/jean/lilies/2.23.80/share/lilypond/2.23.80/ly/scheme-sandbox.ly »
Analyse...
GNU Guile 2.2.7
Copyright (C) 1995-2019 Free Software Foundation, Inc.

Guile comes with ABSOLUTELY NO WARRANTY; for details type `,show w'.
This program is free software, and you are welcome to redistribute it
under certain conditions; type `,show c' for details.

Enter `,help' for help.
scheme@(#{ g101}#)> (macro? define)
While compiling expression:
Syntax error:
unknown location: source expression failed to match any pattern in form 
define
scheme@(#{ g101}#)> (procedure? define)
While compiling expression:
Syntax error:
unknown location: source expression failed to match any pattern in form 
define
scheme@(#{ g101}#)> (macro? (module-ref (current-module) 'define))
$1 = #t
scheme@(#{ g101}#)> (procedure? (module-ref (current-module) 'define))
$2 = #f

C'est mentionné sur cette page :

https://www.gnu.org/software/guile/manual/html_node/Internal-Macros.html

>   Un développeur avait aussi un jour mis des réserves sur la fonction 

Pas idiot.

Par exemple, en Guile 2, les erreurs dans du code exécuté avec
primitive-eval peuvent être difficiles à comprendre. (Je précise
que LilyPond exécute tout le code Scheme avec primitive-eval par défaut,
mais on peut changer ça avec #(ly:set-option 'compile-scheme-code).)

> defined? et primitive-eval.

Ce n'est pas mon point de vue. La question de base qui se pose aux
développeurs LilyPond est : est-ce qu'il est possible de
créer une interface qui fait toujours ce à quoi l'utilisateur
s'attend ? En l'espèce, difficile de savoir ce à quoi l'utilisateur
s'attend. Est-ce que tu t'attendais à voir defined? renvoyer #t
dans un \layout lorsque la variable a été définie dans un autre
\layout dont le \layout où tu utilises defined? hérite ? Peut-être,
peut-être pas, mais que cela t'ait surpris ou non, je suis sûr
qu'il y en a pour qui c'est l'inverse. Même chose avec les
variables locales. Donc s'il faut se méfier de defined?, ce n'est
pas tant parce que son comportement risque de changer dans les
versions futures, même si c'est vrai dans une certaine mesure. C'est
surtout parce que c'est une fonction qui fait pénétrer dans les entrailles
de choix techniques complexes et que par conséquent elle pourrait te
piéger dans des cas un peu avancés si tu n'as pas parfaitement compris
ce qu'elle fait.

>   Pour primitive-eval, ce même développeur avait proposé : (module-ref 

Oui, ça sonne mieux que primitive-eval.

>   Pour defined?, il pourrait y avoir : (module-variable 
> (current-module) sym)

Pour le coup, c'est quasi-équivalent.

Cordialement,
Jean