Re: Scheme pattern for retrieving data in objects

2022-04-03 Thread Jonas Hahnfeld via Discussions on LilyPond development
To add my 2 cents here, bearing in mind that I mostly care about
LilyPond's Scheme code instead of user code in scores:

On Sat, 2022-04-02 at 13:38 +0200, Han-Wen Nienhuys wrote:
> On Fri, Apr 1, 2022 at 6:43 PM Jean Abou Samra  wrote:
> > [...]
> > 
> > Does anyone find a value in defining a macro for this?
> > 
> > \version "2.23.8"
> > 
> > #(define-syntax-rule (fetch obj getter (sym ...) body body* ...)
> > (let ((evald-obj obj)
> >   (evald-getter getter))
> >   (let ((sym (getter obj 'sym))
> > ...)
> > body body* ...)))
> > {
> >c'1
> >\tweak after-line-breaking
> >  #(lambda (grob)
> > (fetch grob ly:grob-property (left-bound-info)
> >   (fetch left-bound-info assoc-ref (common-Y X padding attach-dir)
> > (ly:message "common-Y=~a X=~a padding=~a attach-dir=~a"
> > common-Y X padding attach-dir
> >\startTextSpan
> >c'1\stopTextSpan
> > }
> 
> It's shorter, but is it easier to understand and discover? Is the
> former code (which is somewhat verbose) a real barrier to getting
> coding/typesetting done?

It's actually not a lot shorter, the characters it saves are that the
variable name is identical with the fetched attribute. Which is my
biggest pain point about the proposed macro, because as soon as you
want to or need to name the variable differently (think about naming
elements as children), you cannot use the macro anymore and have to
revert back to the "known" function call. IMHO the bad thing here is
that there are two ways to achieve the same operation, with the new
proposed macro being less powerful.

For my second point, I agree with Dan that defining multiple variables
in one go looks bad, especially in this non-obvious way.


Jonas


signature.asc
Description: This is a digitally signed message part


Re: Scheme pattern for retrieving data in objects

2022-04-03 Thread Jean Abou Samra

Le 02/04/2022 à 13:38, Han-Wen Nienhuys a écrit :

It's shorter, but is it easier to understand and discover? Is the
former code (which is somewhat verbose) a real barrier to getting
coding/typesetting done?

Over the years, I've become extremely wary of syntactic sugar: it adds
an extra barrier to usage/development because everyone not only has to
learn Scheme, they also have to learn the (lilypond specific) idioms
involved.




Yes. On the other hand, LilyPond also has idioms that nobody
would think of *not* using -- I think we can agree that it's
a good thing we write engravers as

(make-engraver
 ((initialize engraver)
    ...)
 (acknowledgers
  ((note-head-interface engraver grob source-engraver)
     ...)))

rather than

`((initialize
   . ,(lambda (engraver)
    ...))
  (acknowledgers
   . ((note-head-interface
       . ,(lambda (engraver grob source-engraver)
        ...)

So I think it depends on the usage frequency and the gain
in ease of use and conciseness. My thinking is that we have
similarly annoying patterns in callbacks in particular (also
music functions). What I'm musing with is essentially
a 'make-callback' in disguise -- something that would eventually
become the natural way to do it, like nobody (I believe)
would think of writing an engraver the alist way.

I'd also say that the nature of Scheme makes it such that
there is not all that much of a difference between syntax
and APIs -- if you wanted a language where the whole game
is not to build a language for your needs on top of the
basics, the time to choose Python was 20 years ago :-)


[Dan]
I'm not enthusiastic about multiple variable declarations in one line 
or variable declarations that don't even look like declarations.



I hear you, but I do not see a preexisting consistent look
for lvalues.


(let ((x ...)
  (y ...)
  (z ...))
  ...)

(lambda (x y z)
  ...)

(define-values (x y z)
  ...)

(match-let* (((x y z) ...))
  ...)



+1.  I can imagine frustration after imitating some working code that 
calls `ly:grob-property` and being asked in review to use `fetch` 
instead because it is better.



Well, at the risk of sounding like a jerk, the same already
applies to auto *const vs. Smob_type *, C++11 member init
vs. initialize (), range-based loops vs. index-based loops, ...
I guess everyone has their favorite languages where they would like
more productive idioms and their less-favorite languages where
they want less features to learn. I am not a stranger to the
learning curve issue either: cf.
https://gitlab.com/lilypond/lilypond/-/merge_requests/1102#note_805070851
From my point of view, the difference is that that it doesn't
take 2 minutes to learn what 'fetch' does once you've read an
example.  (Whereas, searching about C++ lambdas on
cppreference.com, I read things like "The lambda expression is a
prvalue expression of unique unnamed non-union non-aggregate
class type, known as closure type, which is declared (for the
purposes of ADL) in the smallest block scope, class scope, or
namespace scope that contains the lambda expression."
. And I need
to understand a C++ feature that I use or I could introduce flaky
segfaults and the like ...)

Coming back to the topic of reviews: see above, I'd hope to find something
that ultimately becomes more natural a thing to do than the current
idiom.





If the basic syntax is too verbose, could it be revised rather than 
added to?


  (ly:get grob 'X-offset)
  (ly:get grob 'left-bound-info 'padding)
  (ly:get context 'alterationGlyphs 1/2)



A single function operating on many "types" is a Scheme antipattern: since
Scheme only has latent typing, a pure Scheme implementation would have to
test the value against a predefined set of type predicates on every access.
It might be possible to do in C++ with dynamic dispatch on our smob classes,
but it's still not really how Scheme thinks (and it won't work with alists
or ly:grob-object vs. ly:grob-property). A ly:grob-nested-property similar
to ly:grob-set-nested-property! does look reasonable.


---

I've been thinking about this more and came up with something a little
different, as it appeared on examples that the nesting of let* and
fetch was annoying. This version of it is a kitchen sink, basically
serving as an experiment. Ultimately I don't think we'd want a macro
with all of its features, but I'm not sure yet what selected features
could be the most useful. It is an extended version of let*, called
"with", that accepts clauses of the form

(name <= ly:grob-property grob)

which translates into

(name (ly:grob-property grob 'name))

I've chosen "with" for now because it's the same number of characters as 
"let*",
saving from reindenting (the usual issue with switching between let and 
let*).

The use of <= is inspired from => in cond clauses.

There can be several names:

(element elements articulations <= ly:music-property music)

There can be a default:


Re: Scheme pattern for retrieving data in objects

2022-04-02 Thread Luca Fascione
I can see this happening, I thought a bit about it and it seems to me there
are several factors that are potentially at play here:

 - one is how much alignment there is between the API presented to (or
rather, *perceived* by) the user vs their mental model of what is going on
in the program: the more distant these two are, the harder a time the user
will have, and whatever can be made to reduce this gap will feel good to
them. Besides, I'd imagine for most people the library shipped with
lilypond is at least one step removed from their problem space and
vocabulary, and the intervening adaptation layer is likely to feel like a
burden to people. I guess I'm saying that it seems to me lilypond is a bit
like plain TeX and there are many folks that find a layer like LaTeX is
better aligned with their mental models and how they prefer to reason about
their content

 - I'm unclear how technical or interested in coding the lilypond userbase
is: it seems to me that all folks that come to lilypond because the sheets
look great are not necessarily inclined to learn how to program to achieve
their goals, they're after the goal, and the software engineering aspects
of this provide no joy to them. Again, you see something similar in
TeX/LaTeX users as well

 - I myself still find scheme weird: this seems to be largely because it's
just different from everything else I'm familiar with both from a "look"
perspective, but much more importantly from a semantics perspective. I am
aware that as a descendent of Lisp it predates a lot of the common
languages of today, and in a number of ways it's rather more elegant and
better conceived as a language, I am just stating that it's *different*. I
happen to be a very driven person, so I'll muscle through that to get to
the sheets and sheet-making process I want, but I don't know how many other
people have a taste for this or find it enjoyable, I'd imagine most folks
just find it annoying instead. And especially for folks that do this
outside of their work time, being impeded in one's goals for reasons that
bring along an advantage that one may not find material, is unlikely to
feel like a good state of things. Point being: scheme is very different,
and occasionally very subtly so, from languages like python, ruby,
javascript and all the everyday, garden variety stuff folks are likely to
see at work, I don't know how many people will find this as positive trait,
vs how many would find it an undesirable state of things

I totally see wha Han-Wen is saying and I agree that a lateral-shift kinda
thing is not going to be that useful in the long term. However I wrote this
to express why I don't think all these changes necessarily belong in that
category, I see clear cases where plenty of value is provided to users
instead.

Luca


On Sat, Apr 2, 2022 at 2:50 PM Werner LEMBERG  wrote:

>
> >> Over the years, I've become extremely wary of syntactic sugar: it
> >> adds an extra barrier to usage/development because everyone not
> >> only has to learn Scheme, they also have to learn the (lilypond
> >> specific) idioms involved.
> >
> > I'm curious you say that, since my experience is precisely the
> > opposite: I've had far better results "selling" Lilypond to people
> > using syntactic sugar than basically anything else I can
> > identify. The people I’ve "converted" all want to be able to type
> > things like
> >
> >\reverseMusic \foo
> >
> > rather than learning how to write the equivalent function in
> > Scheme. In other words, syntactic sugar keeps them from learning
> > Scheme as opposed to having to learn it.
> >
> > Am I missing something? Is my experience unique?
>
> No, your experience is not unique.  I think that developers and normal
> users (but probably not Scheme wizards) have rather diametral views on
> this topic.
>
>
> Werner
>


-- 
Luca Fascione
Distinguished Engineer - Ray Tracing - NVIDIA


Re: Scheme pattern for retrieving data in objects

2022-04-02 Thread David Kastrup
Kieren MacMillan  writes:

> Hi Han-Wen,
>
>> Over the years, I've become extremely wary of syntactic sugar: it adds
>> an extra barrier to usage/development because everyone not only has to
>> learn Scheme, they also have to learn the (lilypond specific) idioms
>> involved.
>
> I'm curious you say that, since my experience is precisely the
> opposite: I've had far better results "selling" Lilypond to people
> using syntactic sugar than basically anything else I can identify. The
> people I’ve "converted" all want to be able to type things like
>
>\reverseMusic \foo
>
> rather than learning how to write the equivalent function in
> Scheme. In other words, syntactic sugar keeps them from learning
> Scheme as opposed to having to learn it.
>
> Am I missing something? Is my experience unique?

You are talking about different things.  Syntactic sugar says the same
thing but in a more cohesive manner.  That is not the same as assembling
dedicated functionality from suitable components.

Syntactic sugar saves clutter which is not the same as hiding internals.

-- 
David Kastrup



Re: Scheme pattern for retrieving data in objects

2022-04-02 Thread Kieren MacMillan
Hi Werner,

> your experience is not unique.  I think that developers and normal users
> (but probably not Scheme wizards) have rather diametral views on this topic.

Excellent point.
— Kieren


Re: Scheme pattern for retrieving data in objects

2022-04-02 Thread Dan Eble



> On Apr 2, 2022, at 07:38, Han-Wen Nienhuys  wrote:
> 
> On Fri, Apr 1, 2022 at 6:43 PM Jean Abou Samra  > wrote:
...
>> (fetch left-bound-info assoc-ref (common-Y X padding attach-dir)

I'm not enthusiastic about multiple variable declarations in one line or 
variable declarations that don't even look like declarations.


> Over the years, I've become extremely wary of syntactic sugar: it adds
> an extra barrier to usage/development because everyone not only has to
> learn Scheme, they also have to learn the (lilypond specific) idioms
> involved.

+1.  I can imagine frustration after imitating some working code that calls 
`ly:grob-property` and being asked in review to use `fetch` instead because it 
is better.

If the basic syntax is too verbose, could it be revised rather than added to?

  (ly:get grob 'X-offset)
  (ly:get grob 'left-bound-info 'padding)
  (ly:get context 'alterationGlyphs 1/2)

— 
Dan



Re: Scheme pattern for retrieving data in objects

2022-04-02 Thread Werner LEMBERG

>> Over the years, I've become extremely wary of syntactic sugar: it
>> adds an extra barrier to usage/development because everyone not
>> only has to learn Scheme, they also have to learn the (lilypond
>> specific) idioms involved.
> 
> I'm curious you say that, since my experience is precisely the
> opposite: I've had far better results "selling" Lilypond to people
> using syntactic sugar than basically anything else I can
> identify. The people I’ve "converted" all want to be able to type
> things like
> 
>\reverseMusic \foo
> 
> rather than learning how to write the equivalent function in
> Scheme. In other words, syntactic sugar keeps them from learning
> Scheme as opposed to having to learn it.
> 
> Am I missing something? Is my experience unique?

No, your experience is not unique.  I think that developers and normal
users (but probably not Scheme wizards) have rather diametral views on
this topic.


Werner


Re: Scheme pattern for retrieving data in objects

2022-04-02 Thread Kieren MacMillan
Hi Han-Wen,

> Over the years, I've become extremely wary of syntactic sugar: it adds
> an extra barrier to usage/development because everyone not only has to
> learn Scheme, they also have to learn the (lilypond specific) idioms
> involved.

I'm curious you say that, since my experience is precisely the opposite: I've 
had far better results "selling" Lilypond to people using syntactic sugar than 
basically anything else I can identify. The people I’ve "converted" all want to 
be able to type things like

   \reverseMusic \foo

rather than learning how to write the equivalent function in Scheme. In other 
words, syntactic sugar keeps them from learning Scheme as opposed to having to 
learn it.

Am I missing something? Is my experience unique?
Kieren.


Kieren MacMillan, composer (he/him/his)
‣ website: www.kierenmacmillan.info
‣ email: kie...@kierenmacmillan.info




Re: Scheme pattern for retrieving data in objects

2022-04-02 Thread Han-Wen Nienhuys
On Fri, Apr 1, 2022 at 6:43 PM Jean Abou Samra  wrote:
>
> Folks,
>
> Just a random thought in passing: I just wrote something of this kind
> once again:
>
> (let ((elements (ly:music-property m 'elements))
>(element (ly:music-property m 'element))
>(articulations (ly:music-property m 'articulations)))
>...)
>
> (let ((padding (assoc-ref details 'padding))
>(common-Y (assoc-ref details 'common-Y)))
>...)
>
> (let ((padding (ly:grob-property grob 'padding)
>(shorten-pair (ly:grob-property grob 'shorten-pair))
>(normalized-endpoints (ly:grob-property grob 'normalized-endpoints)))
>...)
>
>
> Does anyone find a value in defining a macro for this?
>
>
> \version "2.23.8"
>
> #(define-syntax-rule (fetch obj getter (sym ...) body body* ...)
> (let ((evald-obj obj)
>   (evald-getter getter))
>   (let ((sym (getter obj 'sym))
> ...)
> body body* ...)))
> {
>c'1
>\tweak after-line-breaking
>  #(lambda (grob)
> (fetch grob ly:grob-property (left-bound-info)
>   (fetch left-bound-info assoc-ref (common-Y X padding attach-dir)
> (ly:message "common-Y=~a X=~a padding=~a attach-dir=~a"
> common-Y X padding attach-dir
>\startTextSpan
>c'1\stopTextSpan
> }

It's shorter, but is it easier to understand and discover? Is the
former code (which is somewhat verbose) a real barrier to getting
coding/typesetting done?

Over the years, I've become extremely wary of syntactic sugar: it adds
an extra barrier to usage/development because everyone not only has to
learn Scheme, they also have to learn the (lilypond specific) idioms
involved.

-- 
Han-Wen Nienhuys - hanw...@gmail.com - http://www.xs4all.nl/~hanwen



Re: Scheme pattern for retrieving data in objects

2022-04-01 Thread Jean Abou Samra




Le 01/04/2022 à 18:43, Jean Abou Samra a écrit :


\version "2.23.8"

#(define-syntax-rule (fetch obj getter (sym ...) body body* ...)
   (let ((evald-obj obj)
 (evald-getter getter))
 (let ((sym (getter obj 'sym))
   ...)
   body body* ...)))
{
  c'1
  \tweak after-line-breaking
    #(lambda (grob)
   (fetch grob ly:grob-property (left-bound-info)
 (fetch left-bound-info assoc-ref (common-Y X padding attach-dir)
   (ly:message "common-Y=~a X=~a padding=~a attach-dir=~a" 
common-Y X padding attach-dir

  \startTextSpan
  c'1\stopTextSpan
}


Sorry, should have been:

\version "2.23.8"

#(define-syntax-rule (fetch obj getter (sym ...) body body* ...)
   (let ((evald-obj obj)
 (evald-getter getter))
 (let ((sym (evald-getter evald-obj 'sym))
   ...)
   body body* ...)))
{
  c'1
  \tweak after-line-breaking
    #(lambda (grob)
   (fetch grob ly:grob-property (left-bound-info)
 (fetch left-bound-info assoc-ref (common-Y X padding attach-dir)
   (ly:message "common-Y=~a X=~a padding=~a attach-dir=~a" 
common-Y X padding attach-dir

  \startTextSpan
  c'1\stopTextSpan
}





Scheme pattern for retrieving data in objects

2022-04-01 Thread Jean Abou Samra

Folks,

Just a random thought in passing: I just wrote something of this kind 
once again:


(let ((elements (ly:music-property m 'elements))
  (element (ly:music-property m 'element))
  (articulations (ly:music-property m 'articulations)))
  ...)

(let ((padding (assoc-ref details 'padding))
  (common-Y (assoc-ref details 'common-Y)))
  ...)

(let ((padding (ly:grob-property grob 'padding)
  (shorten-pair (ly:grob-property grob 'shorten-pair))
  (normalized-endpoints (ly:grob-property grob 'normalized-endpoints)))
  ...)


Does anyone find a value in defining a macro for this?


\version "2.23.8"

#(define-syntax-rule (fetch obj getter (sym ...) body body* ...)
   (let ((evald-obj obj)
 (evald-getter getter))
 (let ((sym (getter obj 'sym))
   ...)
   body body* ...)))
{
  c'1
  \tweak after-line-breaking
    #(lambda (grob)
   (fetch grob ly:grob-property (left-bound-info)
 (fetch left-bound-info assoc-ref (common-Y X padding attach-dir)
   (ly:message "common-Y=~a X=~a padding=~a attach-dir=~a" 
common-Y X padding attach-dir

  \startTextSpan
  c'1\stopTextSpan
}


Jean