lua-users home
lua-l archive

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


Hi,

Peter Hill:
> First I implement an indefinite-size number package, "Longnum", that
> includes metamethods for 'add' etc. I then create a complex number
package,
> "Complex", implemented as a pair of Longnum's. Now adding two complex
> numbers together is fine (this is just a normal metamethod) but what if I
> want to create mixed functions that add longnums with complex numbers?

A rather lengthy and sketchy reply.  Hopefully you can pick out some parts
of interest.

I agree that double distpatch is not the most convenient way of implementing
binary operators and I've been toying with a rather similar idea, although
(back then) not related to Lua.  But first let me share some of my
experiences with this (and some Lua 4 code to go with it!)  In Lua I ran
into this operator issue when implementing complex numbers (hey, I'm a
mathematician so I really need those ;-) and some basic linear algebra.
Remarks 1. to 3. below can be classified as "none of the above but in the
same area." [:-)]

1.  Lua numbers should be complex numbers, so they should mix in a natural
way with a complex number package.  I was saved by the bell here because Lua
numbers have no arithmetic tag methods and everything is passed on to the
complex methods which can take care of them by type()-checking.

2.  In the same way I replaced some standard functions like tostring, sin,
cos, etc. by letting them decide by type() which implementation to use.
This works really great.  (And Lua 5 switched to radians, which was my only
gripe in Lua 4 in this respect!)

This did the trick, at least for my purposes, but I agree that the point
here was that native Lua numbers have no tagmethods and the implementation
of complex numbers is rather specific.  You can find the (Lua 4) complex
number package and an example script on my page:

    http://members.chello.nl/~w.couwenberg

Now for the linear algebra part:

3.  Linear algebra is useful over "scalars", which can be Lua numbers,
complex numbers, Gauss integers, large integers, etc.  Now the operator
problem pops up because the multiply is overloaded quite a bit:
matrix*matrix, matrix*vector, vector*matrix, vector*vector, vector*scalar,
matrix*scalar, ...  All of these types can carry their own set of arithmetic
tagmethods.  Note: I did not mention scalar*vector and scalar*matrix.  This
is exactly because of the double dispatching issue: you don't want to
introduce linear algebra stuff into the complex number package (but, see 5.
below)!  On the positive side, I _did_ implement linear algebra without any
reference to complex numbers, while still being able to support them "out of
the box."  (I did not upload the linear algebra package to my page.  It is
fairly basic.  If anyone is interested, just let me know.)

Now, finally, some thoughts on your operator issue.

4.  Note that just registering handlers for all possible argument type
combinations will not solve all issues.  As an example, the complex number
and linear algebra packages must not reference each other, but they _will_
if you must register a mul:[complex-tag]:[vector-tag] operator and such!
Being able to register such operators _outside_ of both packages must at
least leave some access to some methods open, that you would probably want
to keep private to the packages.

5.  In Lua (both 4 and 5) you can do "lazy dispatch" in the following sense.
Lua will direct arithmetic to the left (first) argument if both arguments
have arithmetic methods.  In that left tagmethod, check if you can accept
the tag of the second argument.  If not, then dispatch the operator to the
tag method of the second argument.  Note that the type checking involved is
not really bad: it involves only "known" types.  Come to think of it, this
should have made it in my complex number package...  [:-)].  This scheme
would support complex linear algebra without any cross references (linear
algebra accepts any non-vector/matrix arguments, assuming they are scalars.)

6.  This remark is not specifically related to (or targeted at) Lua and is
_really_ sketchy.  As you suggested, another kind of dispatch for binary
operators might be useful.  I arrived at the notion of "ordering" of types,
where A < B means that instances of A can faithfully be represented as
instances of B.  So "double" < "complex", "integer" < "large integer" etc.
Then instead of registering an arithmetic function for each possible
combination of argument types, this ordering would decide how to handle
mixed arithmetic by "coercion."  You can think of several coercion schemes,
all with their pros and cons.  With regard to issue 4. above: if you
implement linear algebra on an unspecified "scalar" type it should suffice
to declare "complex" < "scalar" at some point to allow complex numbers in
linear algebra operations.  This will not build on package internals.

In the end, I still don't know what would be the "right" way to support
binary operators, even for my own use!  Maybe suggestion 5. is not all bad
as it is simple and can already be implemented in Lua as it stands.  The
trouble with more "powerful" dispatch schemes is that there is often some
other price to pay (performance, implementation, ease of use, ambiguity,
etc.)

One final Lua 5 comment: I _do_ think it is a pity that comparison operators
in Lua are much more "rigid" regarding metatables than arithmetic operators.
(There was a thread on this issue a while ago and Luiz explained the
rationale.)  Not being able to implement __lt between a native lua number
and some sort of "large integer" will be missed... I think...  [:-)]

Bye,
Wim