Re: Data structure for (package) options

2020-01-30 Thread Urs Liska
Am Donnerstag, den 30.01.2020, 09:50 +0100 schrieb Han-Wen Nienhuys:
> On Tue, Jan 28, 2020 at 2:48 PM David Kastrup  wrote:
> 
> > > I think we should aim to avoid textual inclusion as a mechanism
> > > that
> > > powers packaging.
> > 
> > At the implementation side, "textual inclusion" will be what has to
> > power systems where one can "plug in" packages and have them work
> > by
> > being present.  Even Guile's use-modules mechanism works at that
> > level.
> > But of course that doesn't mean that we want \include as a user-
> > level
> > access method in the long haul.
> 
> By textual inclusion, I mean a mechanism that must insert all its
> data
> in the global namespace. Library systems usually must have means to
> be
> explicit about external APIs and internal implementation details. I
> am
> advocating that there is an easy to use mechanism such that OLL
> packages can shield some of the implementation from other packages.
> 

My idea for packages is:

 * packages *always* have .ly files as entry points (even if their
   "real" work is exclusively Scheme). That makes it easier to handle
   (no need to load packages in two different forms) and gives regular
   users a lower threshold of moving library code to packages.
 * they explicitly define their public interface. See below for my
   ideas about that.
 * A new command \usepackage will either load the whole package or only
   a subset of its interface (like the "from X load Y" syntax in
   Python).

Maybe a clean way of making an interface explicit would be not to do

  myFunction = #(define-music-function ...

but having a new (set of?) command(s) that allow

  \export myFunction #(define-music-function

When parsing the package (i.e. loading it for the first time
`myFunction` is stored as the package's interface (in an internal
structure storing the interfaces of all loaded packages, along with
their defined options). [This is something *I* can do with the current
Scheme possibilities]

When using a package that has already been loaded (\usepackage checks
that first) the stored Scheme values (which may equally be variables or
procedures) are made visible in the caller's scope.

The question I have is:
If \usepackage makes the names available to everything else later (like
a regular \include of a LilyPond file) I can do this with what we
already have in Scheme.
If \usepackage should make the names available only within the current
input file (like (use-modules) does) I think \usepackage would have to
be  implemented at the parser level.

Urs




Re: Data structure for (package) options

2020-01-30 Thread Han-Wen Nienhuys
On Tue, Jan 28, 2020 at 2:48 PM David Kastrup  wrote:

> > I think we should aim to avoid textual inclusion as a mechanism that
> > powers packaging.
>
> At the implementation side, "textual inclusion" will be what has to
> power systems where one can "plug in" packages and have them work by
> being present.  Even Guile's use-modules mechanism works at that level.
> But of course that doesn't mean that we want \include as a user-level
> access method in the long haul.

By textual inclusion, I mean a mechanism that must insert all its data
in the global namespace. Library systems usually must have means to be
explicit about external APIs and internal implementation details. I am
advocating that there is an easy to use mechanism such that OLL
packages can shield some of the implementation from other packages.

> It's likely that a typical more complex package may consist of both .ly
> and/or .scm components and that is an implementation detail that a
> package user should not need to bother about, and that hopefully should
> not significantly complicate things for people wanting to provide
> packages with either or both .ly and/or .scm components.
>
> --
> David Kastrup



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



Re: Data structure for (package) options

2020-01-28 Thread Urs Liska



Am 28. Januar 2020 14:48:54 MEZ schrieb David Kastrup :
>Han-Wen Nienhuys  writes:
>
>> On Mon, Jan 27, 2020 at 11:39 PM Urs Liska 
>wrote:
>>>
>>> I didn't have time to really think about much (about LilyPond) the
>past
>>> week, just enjoyed seeing so much constructive discussion.
>>> [..]
>>
>> I haven't read your messages in detail, but I'd like to throw out one
>> thought to consider: we use GUILE modules as a mechanism for
>> identifier namespacing (\paper, \header all create modules). I think
>> it might be useful if we could provide a LilyPond native mechanism
>for
>> packaging that declares a namespace implicitly, eg.
>>
>>   \module "edition" {
>>   internal = ...
>>   addTweak  = ...
>>   }
>>
>>   \import edition.addTweak
>>
>> that would let you define constructs in a package that doesn't
>pollute
>> the global namespace, and let .ly packages control explicitly what
>> symbols they want to use.
>>
>> I think we should aim to avoid textual inclusion as a mechanism that
>> powers packaging.
>
>At the implementation side, "textual inclusion" will be what has to
>power systems where one can "plug in" packages and have them work by
>being present.  Even Guile's use-modules mechanism works at that level.
>But of course that doesn't mean that we want \include as a user-level
>access method in the long haul.
>
>It's likely that a typical more complex package may consist of both .ly
>and/or .scm components and that is an implementation detail that a
>package user should not need to bother about, and that hopefully should
>not significantly complicate things for people wanting to provide
>packages with either or both .ly and/or .scm components.

From my experience with OLL it would sesm reasonable to have all packages use 
.ly files as entry point. From there they can use Scheme modules if they need 
to. OLL does some work to add its root to the guile path, so there's the way to 
use modules relstive to the package.

Urs


-- 
Diese Nachricht wurde von meinem Android-Gerät mit K-9 Mail gesendet.



Re: Data structure for (package) options

2020-01-28 Thread David Kastrup
Han-Wen Nienhuys  writes:

> On Mon, Jan 27, 2020 at 11:39 PM Urs Liska  wrote:
>>
>> I didn't have time to really think about much (about LilyPond) the past
>> week, just enjoyed seeing so much constructive discussion.
>> [..]
>
> I haven't read your messages in detail, but I'd like to throw out one
> thought to consider: we use GUILE modules as a mechanism for
> identifier namespacing (\paper, \header all create modules). I think
> it might be useful if we could provide a LilyPond native mechanism for
> packaging that declares a namespace implicitly, eg.
>
>   \module "edition" {
>   internal = ...
>   addTweak  = ...
>   }
>
>   \import edition.addTweak
>
> that would let you define constructs in a package that doesn't pollute
> the global namespace, and let .ly packages control explicitly what
> symbols they want to use.
>
> I think we should aim to avoid textual inclusion as a mechanism that
> powers packaging.

At the implementation side, "textual inclusion" will be what has to
power systems where one can "plug in" packages and have them work by
being present.  Even Guile's use-modules mechanism works at that level.
But of course that doesn't mean that we want \include as a user-level
access method in the long haul.

It's likely that a typical more complex package may consist of both .ly
and/or .scm components and that is an implementation detail that a
package user should not need to bother about, and that hopefully should
not significantly complicate things for people wanting to provide
packages with either or both .ly and/or .scm components.

-- 
David Kastrup



Re: Data structure for (package) options

2020-01-28 Thread David Kastrup
Urs Liska  writes:

> Am Dienstag, den 28.01.2020, 00:34 +0100 schrieb David Kastrup:
>> Urs Liska  writes:
>> 
>> So basically there is long-term potential for efficiency to mostly
>> become a non-issue.
>
> I'm sorry, but I don't fully understand what you are trying to tell me
> here. Are you saying that you already had some internal changes in mind
> and that storing the package options in an alist is something I
> shouldn't really worry about?

a) the storage format is something you should not worry about with
   regard to efficiency
b) xxx.xxx.xxx syntax for organising material should not be shied away
   from if convenient since it is comparatively straightforward to stop
   alists from being a mandatory underlying data structure
c) don't choose between tree or flat organisation for reasons of
   efficiency: only take user convenience into account

> BTW: I mistook my own example (shouldn't have started writing late in
> the evening ...). What I meant is storing the options in objects, not
> assigning symbols:
>
>   (define package-options
>`((edition-engraver . ,ee-options)
>  (scholarly . ,scholarly-options)
>  ...
> ))
>
> with ee-options and scholarly-options having been bound to tree objects
> before.

This prescribes a particular data structure.

package-options.edition-engraver = #ee-options
package-options.scholarly = #scholarly-options

in contrast will at the _current_ point of time use a particular data
structure but we can do a switcheroo underneath in some near future.

>> > It seems reasonable to not store a package's options as a nested
>> > alist, though. I'd rather consider using a tree implementation in a
>> > class, which would for example make it cleaner to encapsulate the
>> > behaviour and e.g. implement type checking in that class rather
>> > than in the accessor functions, or enable alternative tree
>> > implementations if that should become interesting for custom data
>> > storage.  We have a tree implementation in oll-core at
>> > https://github.com/openlilylib/oll-core/blob/master/scheme/oll-core/tree.scm
>> > Would that be something to use here?
>> 
>> Whatever we do, it should be an implementation detail the user _and_
>> the package programmers do not really need to know about.  There
>> should be accessors hiding the organisation.
>
> Yes, that's what I intended. It should be a tree implementation with a
> given characteristic that basically noone should bother about. There
> should be the *possibility* to drop-in a different tree implementation
> (with the same interface) if for whatever reason a package might want
> to do so (I could imagine that for some algorithmic use of the data it
> might be reasonable to consider some sort of self-organizing tree to
> optimize access times - but definitely not for the bread-and-butter
> use case of package options).

I think that our basic data structures should be accessible at the
LilyPond syntax level without even going down into Scheme.

-- 
David Kastrup



Re: Data structure for (package) options

2020-01-28 Thread Urs Liska
Am Dienstag, den 28.01.2020, 09:26 +0100 schrieb Han-Wen Nienhuys:
> On Mon, Jan 27, 2020 at 11:39 PM Urs Liska 
> wrote:
> > I didn't have time to really think about much (about LilyPond) the
> > past
> > week, just enjoyed seeing so much constructive discussion.
> > [..]
> 
> I haven't read your messages in detail, but I'd like to throw out one
> thought to consider: we use GUILE modules as a mechanism for
> identifier namespacing (\paper, \header all create modules). I think
> it might be useful if we could provide a LilyPond native mechanism
> for
> packaging that declares a namespace implicitly, 


That sound similar to what David suggested in 
https://lists.gnu.org/archive/html/lilypond-devel/2020-01/msg00349.html

> > Ok.  One thing to think about is that we want package files to be
> > contributed by "ordinary" users.  But something like
> >
> > \exportSymbols transposeSequence,instrumentGroup,scratchMyBack
> >
> > would be perfectly feasible syntactical sugar.

and following conversation on that thread.

I'm all for such an encapsulation strategy for packages.

> eg.
> 
>   \module "edition" {
>   internal = ...
>   addTweak  = ...
>   }

I think this concrete suggestion *looks* nice but would be awkward to
handle and impose restriction on how package authors can express
themselves.

I'd prefer (IISC more to David's suggestion) being able to write a
package as a regular .ly or .scm file with commands to explicitly
export/expose selected functions and variables.

> 
>   \import edition.addTweak

In analogy to Python one could have a choice between

  \import edition

for making all exported names visible or

  \import \with {
addTweak.foo.bar
  } edition

for selectively importing three names.
(the \with being my current abuse of ly:context-mod?)

I have no strong opinion, and \import seems like a natural choice with
a programmer's eye, but \usepackage might be more expressive from a
document author's perspective.

> 
> that would let you define constructs in a package that doesn't
> pollute
> the global namespace, and let .ly packages control explicitly what
> symbols they want to use.
> 
> I think we should aim to avoid textual inclusion as a mechanism that
> powers packaging.
> 

That's a good point but will definitely be beyond what *I* can do.

Urs





Re: Data structure for (package) options

2020-01-28 Thread Han-Wen Nienhuys
On Mon, Jan 27, 2020 at 11:39 PM Urs Liska  wrote:
>
> I didn't have time to really think about much (about LilyPond) the past
> week, just enjoyed seeing so much constructive discussion.
> [..]

I haven't read your messages in detail, but I'd like to throw out one
thought to consider: we use GUILE modules as a mechanism for
identifier namespacing (\paper, \header all create modules). I think
it might be useful if we could provide a LilyPond native mechanism for
packaging that declares a namespace implicitly, eg.

  \module "edition" {
  internal = ...
  addTweak  = ...
  }

  \import edition.addTweak

that would let you define constructs in a package that doesn't pollute
the global namespace, and let .ly packages control explicitly what
symbols they want to use.

I think we should aim to avoid textual inclusion as a mechanism that
powers packaging.

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



Re: Data structure for (package) options

2020-01-27 Thread Urs Liska
Am Dienstag, den 28.01.2020, 00:34 +0100 schrieb David Kastrup:
> Urs Liska  writes:
> 
> > I didn't have time to really think about much (about LilyPond) the
> > past
> > week, just enjoyed seeing so much constructive discussion.
> > 
> > I think the first thing I'd like to go for with the
> > package/extension
> > mechanism is storing and handling (package) options.
> > 
> > There are three use cases which are differently closely related to
> > the
> > actual package mechanism but should be dealt with together:
> > 
> >  * package options
> > * specified, typed and preset with default values by a package
> > * handled by getter and setter functions
> >  * custom options
> > * behave like package options
> > * not associated with a package but explicitly called for in
> > user
> >   code
> >  * custom data
> > * use the option syntax/infrastructure to store arbitrary data
> > * for example a music tree as described in Jan-Peter's
> > templating
> > 
> > I think package options should be handled in one association list,
> > hidden in a Scheme module and only accessed by specific accessor
> > code.
> > The list elements should have the package name as car and an option
> > *object* as cdr:
> > 
> >   (define package-options
> >'((edition-engraver . ee-options)
> >  (scholarly . scholarly-options)
> >  ...
> > ))
> > 
> > I expect the number of packages loaded in a document ranging from
> > "a
> > few" to "a few dozens", so probably a simple alist should be the
> > right
> > data structure?
> 
> Before we devolve into an efficiency discussion here, let me sketch
> one
> of my "should make sense" projects": our access of alists mostly is
> through our own accessor function ly:assoc-get .  If the elements on
> an
> alist could not just be a key-value pair but a hashtable (for looking
> up
> key-value, of course) or a vector (I think I'd prefer it being 1-
> based
> in spite of making the Scheme indexing less obvious), our lookup
> routines should be able to deal with that.  Some alists could be
> compressed on use, making things like drumtables and note alists
> efficient behind the scenes.
> 
> Such a replacement would work transparently for things like
> 
> package-options.edition-engraver = #'ee-options
> 
> or similar.  So basically there is long-term potential for efficiency
> to
> mostly become a non-issue.

I'm sorry, but I don't fully understand what you are trying to tell me
here. Are you saying that you already had some internal changes in mind
and that storing the package options in an alist is something I
shouldn't really worry about?

BTW: I mistook my own example (shouldn't have started writing late in
the evening ...). What I meant is storing the options in objects, not
assigning symbols:

  (define package-options
   `((edition-engraver . ,ee-options)
 (scholarly . ,scholarly-options)
 ...
))

with ee-options and scholarly-options having been bound to tree objects
before.

> 
> > It seems reasonable to not store a package's options as a nested
> > alist,
> > though. I'd rather consider using a tree implementation in a class,
> > which would for example make it cleaner to encapsulate the
> > behaviour
> > and e.g. implement type checking in that class rather than in the
> > accessor functions, or enable alternative tree implementations if
> > that
> > should become interesting for custom data storage.
> > We have a tree implementation in oll-core at 
> > https://github.com/openlilylib/oll-core/blob/master/scheme/oll-core/tree.scm
> > Would that be something to use here?
> 
> Whatever we do, it should be an implementation detail the user _and_
> the
> package programmers do not really need to know about.  There should
> be
> accessors hiding the organisation.
> 

Yes, that's what I intended. It should be a tree implementation with a
given characteristic that basically noone should bother about. There
should be the *possibility* to drop-in a different tree implementation
(with the same interface) if for whatever reason a package might want
to do so (I could imagine that for some algorithmic use of the data it
might be reasonable to consider some sort of self-organizing tree to
optimize access times - but definitely not for the bread-and-butter use
case of package options).

Urs





Re: Data structure for (package) options

2020-01-27 Thread David Kastrup
Urs Liska  writes:

> I didn't have time to really think about much (about LilyPond) the past
> week, just enjoyed seeing so much constructive discussion.
>
> I think the first thing I'd like to go for with the package/extension
> mechanism is storing and handling (package) options.
>
> There are three use cases which are differently closely related to the
> actual package mechanism but should be dealt with together:
>
>  * package options
> * specified, typed and preset with default values by a package
> * handled by getter and setter functions
>  * custom options
> * behave like package options
> * not associated with a package but explicitly called for in user
>   code
>  * custom data
> * use the option syntax/infrastructure to store arbitrary data
> * for example a music tree as described in Jan-Peter's templating
>
> I think package options should be handled in one association list,
> hidden in a Scheme module and only accessed by specific accessor code.
> The list elements should have the package name as car and an option
> *object* as cdr:
>
>   (define package-options
>'((edition-engraver . ee-options)
>  (scholarly . scholarly-options)
>  ...
> ))
>
> I expect the number of packages loaded in a document ranging from "a
> few" to "a few dozens", so probably a simple alist should be the right
> data structure?

Before we devolve into an efficiency discussion here, let me sketch one
of my "should make sense" projects": our access of alists mostly is
through our own accessor function ly:assoc-get .  If the elements on an
alist could not just be a key-value pair but a hashtable (for looking up
key-value, of course) or a vector (I think I'd prefer it being 1-based
in spite of making the Scheme indexing less obvious), our lookup
routines should be able to deal with that.  Some alists could be
compressed on use, making things like drumtables and note alists
efficient behind the scenes.

Such a replacement would work transparently for things like

package-options.edition-engraver = #'ee-options

or similar.  So basically there is long-term potential for efficiency to
mostly become a non-issue.

> It seems reasonable to not store a package's options as a nested alist,
> though. I'd rather consider using a tree implementation in a class,
> which would for example make it cleaner to encapsulate the behaviour
> and e.g. implement type checking in that class rather than in the
> accessor functions, or enable alternative tree implementations if that
> should become interesting for custom data storage.
> We have a tree implementation in oll-core at 
> https://github.com/openlilylib/oll-core/blob/master/scheme/oll-core/tree.scm
> Would that be something to use here?

Whatever we do, it should be an implementation detail the user _and_ the
package programmers do not really need to know about.  There should be
accessors hiding the organisation.

-- 
David Kastrup



Data structure for (package) options

2020-01-27 Thread Urs Liska
I didn't have time to really think about much (about LilyPond) the past
week, just enjoyed seeing so much constructive discussion.

I think the first thing I'd like to go for with the package/extension
mechanism is storing and handling (package) options.

There are three use cases which are differently closely related to the
actual package mechanism but should be dealt with together:

 * package options
* specified, typed and preset with default values by a package
* handled by getter and setter functions
 * custom options
* behave like package options
* not associated with a package but explicitly called for in user
  code
 * custom data
* use the option syntax/infrastructure to store arbitrary data
* for example a music tree as described in Jan-Peter's templating

I think package options should be handled in one association list,
hidden in a Scheme module and only accessed by specific accessor code.
The list elements should have the package name as car and an option
*object* as cdr:

  (define package-options
   '((edition-engraver . ee-options)
 (scholarly . scholarly-options)
 ...
))

I expect the number of packages loaded in a document ranging from "a
few" to "a few dozens", so probably a simple alist should be the right
data structure?

Package and custom config options are organized as a tree. From my
experience so far the nodes in that tree have 1-10 children each, while
I can imagine this to be 20 to 30 or even 50 in rare cases, but surely
not more.

It seems reasonable to not store a package's options as a nested alist,
though. I'd rather consider using a tree implementation in a class,
which would for example make it cleaner to encapsulate the behaviour
and e.g. implement type checking in that class rather than in the
accessor functions, or enable alternative tree implementations if that
should become interesting for custom data storage.
We have a tree implementation in oll-core at 
https://github.com/openlilylib/oll-core/blob/master/scheme/oll-core/tree.scm
Would that be something to use here?

My idea is to put that in an .scm file, say, scm/options.scm, and the
accessor code in a file ly/options.ly.

Additionally there's a file ly/packages.ly that will define a
\usepackage command. \usepackage will check if there's already an
element in the options alist for the given package name, and if not it
will create one with a new tree object. The package file is then free
to register options which will implicitly be stored in the tree object.

Does that look like a viable approach?

Urs