lua-users home
lua-l archive

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




Roberto Ierusalimschy escribió:

> I think it is important to separate unified methods in two quite
> independent parts: the first part is keeping tag methods in a table (what
> you call "exposing it as tables"); but there is a the second part: to
look
> into this table for "methods" when you use the syntax a:foo().

> (The first part is actually simpler than the current tag system, and we
are
> considering adopting it in the next version of Lua. It is very easy to
> implement the current system using the new one; and we will do it, to
keep
> compatibility).

Actually, I think there is another take on this. The following are unedited
thoughts written just before going to lunch, and I won't be offended if
anyone thinks that they were actually out to lunch. Still, you never know,
it might spark some interesting debate.

As I understand it from the correspondence (and no doubt Edgar will correct
me if I'm wrong), in his unified methods implementation,

a:foo(x)

is "syntactic sugar" for:

methods(a).foo(a,x)

where methods(a) is some sort of metamethod, I would guess. (Or perhaps it
works recursively, which might be cool if confusing.)

Another alternative would be for

a:foo

to be "syntactic sugar" for:

curry1(methods(a), a)

where curry1 is a language primitive whose behaviour is defined by:

function(fn, arg) return function(...) return call (fn, tinsert(arg, 1,
args) or args) end end

(I think there is a better way of writing that in 4.1. The irritating "or
args" compensates for tinsert not returning it's first argument.)

It is easy enough to imagine an efficient implementation of this. To start
with, one could implement curry1 as a language primitive (or even the more
general version which accepts more than one curried argument); it would
essentially be the 4.0 implementation of C-closures with the closures
arguments showing up at the bottom of the stack instead of the top.

A further improvement would be for the compiler to recognise a:foo(x, y, z)
and optimise away the creation of the curried function.

Really, this makes the : in a:foo a pseudo-operator with a known and simple
optimisation rather than "syntactic sugar" (that is, it buries the
definition of the operator "methods"). Personally, I might choose a
different symbol for this operator to avoid confusing it with any current
definition of ":". At the risk of offending someone's sensibilities, I'll
write it as "->". It's a pseudo-operator in the sense that "." is a
pseudo-operator; that is, its right-hand argument is a literal.

Now,

a.foo     is defined as  a["foo"]

and

a[x] is defined as  gettable_event(a, x)
a[x] = v  is defined as  settable_event(a, v, x)

So, by analogy,

a->foo    is defined as  a<<"foo">>

(I originally wrote that as a«"foo"» but guillemots are not so easy to
type)

and

a<<x>>         is defined as  getmethod_event(a, x)
a<<x>> = v     is defined as  setmethod_event(a, v, x)

Taking this additional step makes the optimisation referred to above a
little harder to do, since we can no longer rely on a->x returning a curry.
We could make the optimisation explicit by additionally defining:

a<<x>>(args)   is defined as  callmethod_event(a, x, args)

although I find this a little unpleasant because the whole point of this
idea was to do away with special-casing method calls. Instead, we could
still optimise by verifying whether a<<x>> (or a->foo) returned a curry,
and if so immediately expanding it onto the stack before appending the
remaining arguments; this would result in the curry being created and soon
destroyed, but it would avoid a bit of stack shuffling. Alternatively, one
could simply not worry about it.

Is this useful? ... I think so, but any examples will have to wait...

R.