[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Updating alists (was: Tenuto marking too close to note)

From: Jean Abou Samra
Subject: Re: Updating alists (was: Tenuto marking too close to note)
Date: Thu, 30 Dec 2021 22:20:41 +0100
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Thunderbird/91.3.1

Le 30/12/2021 à 12:36, Lukas-Fabian Moser a écrit :
It's much worse than that: It's not the first time _I've_ been tripped up by this (previously it was with sort!). I'll just have to write 100 copies of

"The exclamation mark in something! does not mean the function is guaranteed to make the desired change in-place."

on my wall.


For assoc-set! and friends, I actually wonder why this
is since when the function has exhausted the alist in
its search for a matching pair, it could well set the
last cdr to add the new pair at the end. That would make
the interface somewhat more convenient. But libguile/alist.c
writes them as, for example,

SCM_DEFINE (scm_assq_set_x, "assq-set!", 3, 0, 0,
            (SCM alist, SCM key, SCM val),
  SCM handle;

  handle = scm_sloppy_assq (key, alist);
  if (scm_is_pair (handle))
      scm_set_cdr_x (handle, val);
      return alist;
    return scm_acons (key, val, alist);

which conses the new pair at the beginning instead.

But more seriously: Thanks! And also for advertising ice-9 match - it's really a different coding style from the more commonly seen (if pair? ...) constructs.

I have been really liking pattern matching since
I started to more seriously program in OCaml. It
clarifies code by showing what the programmer thinks
in a visual way. More and more languages support
it in some form these days, e.g. Rust has it (OK,
ML-inspired language), and more recently Python gained
it (with one small syntax suggestion from yours

* * *

Another question: With the (hopefully) upcoming changes in the script-alist structure (using symbols instead of strings as keys), we're quite close to being able to simply do

\version "2.23.6"

myScripts = \default-script-alist
myScripts.tenuto.padding = 5

\layout {
  \context {
    scriptDefinitions = #myScripts


(One might also skip defining a new variable and instead change default-script-alist directly, but it has to be re-read in a \layout block anyway.)

But at the moment, this does not work because the changed key is not being replaced in the alist but added at the beginning. If I see things correctly, this is the remark about "not coalescing multiple overrides of the same property" in nested_property_alist(...) in ily/

Re-enabling the outcommented branch in that function (taking care to use assoc_tail instead of assq_tail and correcting the order of arguments to that call) basically works, but there's trouble ahead in the case where one does

variable.entry = 15
variable.entry.first = #'i-ve-decided-i-want-a-sublist-after-all

which unfortunately is what happens if one \override's a nested property given by a callback (e.g. VerticalAxisGroup.staff-staff-spacing), or worse

variable.entry = #'(some . pair)
variable.entry.first = #'i-ve-decided-i-want-a-sublist-after-all

since an entry that is a pair looks like the beginning of a sublist.

Both of these cases seem to work the same as in
current versions if I do

diff --git a/lily/ b/lily/
index 9c158561b1..69d8219f2a 100644
--- a/lily/
+++ b/lily/
@@ -49,7 +49,7 @@ partial_list_copy (SCM lst, SCM tail, SCM newtail)
 assq_tail (SCM key, SCM alist, SCM based_on = SCM_EOL)
-  for (SCM p = alist; !scm_is_eq (p, based_on); p = scm_cdr (p))
+  for (SCM p = alist; scm_is_pair (p) && scm_is_pair (scm_car (p)) && !scm_is_eq (p, based_on); p = scm_cdr (p))
       if (scm_is_eq (scm_caar (p), key))
         return p;
@@ -60,7 +60,7 @@ assq_tail (SCM key, SCM alist, SCM based_on = SCM_EOL)
 assv_tail (SCM key, SCM alist, SCM based_on = SCM_EOL)
-  for (SCM p = alist; !scm_is_eq (p, based_on); p = scm_cdr (p))
+  for (SCM p = alist; scm_is_pair (p) && scm_is_pair (scm_car (p)) && !scm_is_eq (p, based_on); p = scm_cdr (p))
       if (scm_is_true (scm_eqv_p (scm_caar (p), key)))
         return p;
@@ -75,7 +75,7 @@ assoc_tail (SCM key, SCM alist, SCM based_on = SCM_EOL)
     return assq_tail (key, alist, based_on);
   if (scm_is_number (key) || scm_is_true (scm_char_p (key)))
     return assv_tail (key, alist, based_on);
-  for (SCM p = alist; !scm_is_eq (p, based_on); p = scm_cdr (p))
+  for (SCM p = alist; scm_is_pair (p) && scm_is_pair (scm_car (p)) && !scm_is_eq (p, based_on); p = scm_cdr (p))
       if (ly_is_equal (scm_caar (p), key))
         return p;
@@ -156,12 +156,12 @@ nested_property_alist (SCM alist, SCM prop_path, SCM value)
   // Outcommented code would coalesce multiple overrides of the same
   // property
-#if 0
-  SCM where = assq_tail (alist, key);
+  //#if 0
+  SCM where = assoc_tail (key, alist);
   if (scm_is_true (where))
     return scm_acons (key, value,
                       partial_list_copy (alist, where, scm_cdr (where)));
+  //#endif
   return scm_acons (key, value, alist);

But I really don't have deep enough an insight on this code
to judge, sorry.

On the other hand, I would expect people who are using

Violin.I = { \someMusic }
Violin.I = { \someNewMusic }

would indeed like to be their first definition to actually be replaced (even if using \Violin.I in a score happens to produce only the most recent entry).

I can't predict all consequences, but I find
it conceivable.

you'll get across-session bleed of assignments when making them
destructive.  Another option is, of course, to do what amounts to an
in-place modification of a structural copy.

The outcommented code path mentioned by Lukas does
not mutate the alist pair but copies the spine to
form a new alist sharing a tail with the original.
So yes, there is copy (but shallow and only the spine).
Not that I will teach this to the author of this
code, but for the list at large ...


reply via email to

[Prev in Thread] Current Thread [Next in Thread]