On Wed, Jul 17, 2013 at 2:45 AM, Bruno Le Floch <[log in to unmask]> wrote:

> A "type" is defined by setting up a property list (two actually)
> matching a word (new:N, put_right:Nn, tail:n, ...) with a function
> which implements it for that type (\seq_new:N, \seq_put_right:Nn,
> \ERROR, ...).  An "object" of that type is simply a token list
> starting with a specific marker \s__<type> and ending with \s_stop.
> This means that one can manipulate such objects (props, seqs, etc.) as
> n-type arguments, rather than the current constraint that they be of
> N-type always.

This makes sense. A type contains method implementations, an object
contains data.

> Functions to manipulate the object can still be called directly as
> they are right now, with no overhead.  They can also be called through
> an \obj:Nn function, which takes care of finding the type of the first
> argument.  E.g., perhaps,
>     \prop_new:N \l_my_prop
>     \prop_put:Nnn \l_my_prop { key } { value }
>     \iow_term:x { \obj:Nn \l_my_prop { get:Nn = { key } , head:n } }
>     \obj:Nn \l_my_prop
>       {
>         pop:NnN = { key } ,
>         tail:N ,
>         map_inline:Nn = { \iow_term:n {#1} }
>       }

There's more going on here than a shorter syntax and type-derivation
for the first argument. You're chaining method calls. In a typical
object oriented language it might look like this:

my_prop .pop(key) .tail() .map_inline( λ x, iow_term(x) );

A few notes:

(1)  Implementation wise, the link between pop and tail is quite
different from the link between tail and map_inline. The former is
through a temporary variable, the latter is through the value of the
expression itself (i.e. the input stream). You'll want to be clear and
consistent on this.

(2)  Your earlier description suggests that "methods" are not
automatically derived from existing functions. If that's the case, I
suggest you drop the argument specifiers that are not explicitly used,

\obj:Nn \l_my_prop
    pop:n = { key } ,
    map_inline:n = { \iow_term:n {#1} }

(3)  And while we're at it, why stick to the key/value notation with
the "{}", "," and "="? A feature this fundamental deserves a dedicated
syntax. You could probably make the following syntax work:

\obj:Nn \l_my_prop . pop:n { key } . tail: . map_inline:n { \iow_term:n {#1} }

It doesn't look like \obj:Nn is going to be expandable anyway. The
above version scans ahead. It first expects a variable, then a dot,
then a method name. Based on the method name, it will retrieve the
expected parameters. It then scans for another dot. Lather, rinse,

> Now, in principle many of the operations performed on a given type can
> be built from very few building blocks.  One such important block for
> objects over which we can map extracts the first item, and the
> remainder of the object (as an object of the same type), and leaves
> the two parts as brace groups.  Specifically, we want a
> "\something_split:nNF" function which takes as arguments an object #1
> of type <something>, a two-argument function #2, and some tokens #3,
> and outputs #2 followed by the two groups obtained from #1 if
> extracting the head was successful (the <something> was non-empty) or
> #3 if the <something> was empty.  From such a function, one can derive
> mapping functions, a get_left function, a get_right function, and all
> sorts of jolly things.

You're suggesting a possible layout for the data in an object,
allowing easier recursion.

For some types this will make a lot of sense. For others it may not. I
would support —but not enforce— something like this.

> In practice, this would be done by letting a type defer to another for
> unknown methods.  There would be a built-in <iterable> type, from
> which the <something> type could derive.

This is just a specific example of how objects might be used, yes? Not
a fundamental part of them? You'll want to make sure that all of this
can work together seamlessly with existing expl3 functions.

You'll still have to make the choice of whether to support nominal
subtyping or structural subtyping: