Charles Hixson said:
This is one place that I don't think Eiffel got things quite right.
Precedence should be defineable, not necessarily higher than all
built-in operators. If one is defining a cross-product, e.g., one
doesn't necessarily want it to be of higher precedence than whatever one
is using to define the literal representation of the matrix. And
ordinary arithmetic might be of even higher precedence. E.G.:
| .< 1 + 3, 5 * 2, 6 / 3 >. .<n21, n22, n23>. .<n31, n32, n33>. | .*. M
Hmmmm ... I'm not sure that the .<, >. and | are technically operators
(certainly not binary operators). They seem to be merely syntactical
markers.
The problem with definable precedence is that precedence is a compile time
operation and the selection of the operator implementation is a run time
thing. Consider ...
x @a y @b z
(note: Syntax is Eiffel style syntax, so @a and @b are operators. Ignore
for the moment that this conflicts with Ruby syntax for instance variables
and go with me here).
Should this be parsed ((x @a y) @b z) or (x @a (y @b z))? Operators are
generally transformed into method calls, e.g. (x @a y) => (x.@a(y)). So
we look at the runtime class of x to find the operator and determine the
precedence. Unfortunately, at the time the expression is compiled the
runtime class of x isn't available! Indeed, x may have a different class
every time the expression is executed.
So, that leaves us two choices: (1) Reparse the expression everytime it is
executed, or (2) globally define the precedence of an operator across all
classes.
(1) seems a bit too dynamic for my taste, and (2) is troublesome when you
have more than one class defining the operator.
Given that, I wouldn't mind user defined operators in Ruby under the
following conditions...
(A) The operators are visually distinctive. In Eiffel, all user defined
operators must begin with a limited set of symbols (@, #, &, and | IIRC).
After the initial symbol, any printable, non-space character is allowed.
So @in, @cross, &and, #..# are all recognized as operators according to
the Eiffel rules.
(B) Fixed precedence. User defined unary operators should have the same
precedence as other unary operators. User defined Binary operators should
be the same as the highest precedence binary operator under the unary
operators.
Point A means that I don't have to parse a random string of symbols in my
head and decide whether it is legal Ruby syntax or user defined operators
(or Beetle Baily swearing). Ruby's problem is that all the punctuation
characters are already used for other purposes, it would be difficult to
set aside yet another character for this purpose. Perhaps something like
@op@ can be used.
Regarding the matrix example, I find it less than compiling, especially
when existing Ruby can be used to get something quite readable ...
Matrix [
[ 1 + 3, 5 * 2, 6 / 3 ],
[ n21, n22, n23 ],
[ n31, n32, n33 ]
].cross M