From: jlg@cochiti.lanl.gov (J. Giles)
Subject: Re: adding new operators on the fly
Organization: Los Alamos National Laboratory
Date: Wed, 18 Nov 1992 21:23:23 GMT


First: an operator is shorthand for a function call.  So, issues of
type compatibility are settled the same way those issues are handled
by your function protocols.  If these are inadequate, your language
needs work even if you *don't* allow user defined operators.  Further,
this means that operators are expanded inline only if you have such
a mechanism defined for function calls.  The operator's function
can be written in assembly only if your ordinary functions can be.
Etc..  To have an assembly function expanded inline requires that
you have some assembler/processor which makes assembly functions
into intermediate code which is suitable for you compiler to use
(or, you define pseudo-assembly language for each target machine).
This is not all that hard - but it's unconventional - so there's
probably a lot of nay-sayers out there who'll claim it can't be done.

Second: the declaration of an operator should state a) what the operator
looks like (what sequence of characters are to be recognized as the
operator); b) what the associativity of the operator is (defaults to
non-associative); c) what the commutativity of the operator is (defaults
to non-commutative); d) what the precedence of the operator is (if the
language allows the user to define precedence - I personally think all
user defined operators should have the same precedence); e) what the
fixity of the operator is: infix, prefix, or postfix (the number of
arguments is decided by the fixity); f) and finally, the name of the
function which performs the operation.

Note: the associativity choices are:

    Not associative - that is "a @ b @ c" is not even allowed without 
       parenthesis

    left associative - "a @ b @ c" is the same as "(a @ b) @ c"

    right associative - "a @ b @ c" is the same as "a @ (b @ c)"

    associative - the order is left to the compiler's discretion

    anti-associative - "(a @ b) @ c" is the same as "-(a @ (b @ c))"

Commutativity choices are:

    Not commutative - "a @ b" does not equal "b @ a"

    commutative - "a @ b" equals "b @ a"

    anti-commutative - "a @ b" equals "-(b @ a)"

The commutativity choices are handy for declaring overloaded math 
operators.  For example, the mixed mode complex and real multiply
can be defined as (in a pseudo-fortran syntax):

      associative commutative infix operator `*' is
      complex function cfmult(complex::a, real::b)
         return complex(real(a)*b, imag(a)*b)
      end cfmult

Since the operator is declared commutative, this definition handles
the mixed-mode operation even when the real part goes first.  You don't
have to declare additional functions for each possible order of the
operands.  This is particularly useful with union types:

      type scalar_number is union(real, integer)

      associative commutative infix operator `*' is
      complex function cfmult(complex::a, scalar_number::b)
         return complex(real(a)*b, imag(a)*b)
      end cfmult

This defines mixed-mode multiply for complex with *either* real
or integer numbers in either order of the operands appearing.
In many languages, this would require four separate functions
to be defined which differ only in their declarations.

The declaration that `*' is associative means that the compiler
is free to choose evaluation order (if parenthesis are absent).
So, "x * cz * y" where `x' and `y' are real and `cz' is complex
might be done as (using both associativity and commutativity)
"(x * y) * cz" with some possible speedup.

-- 
J. Giles
Newsgroups: comp.arch,comp.lang.misc