On Fri, 26 Mar 2010 16:09:37 -0700 (PDT), James Harris wrote:

On 26 Mar, 08:46, "Dmitry A. Kazakov" <mail...@xxxxxxxxxxxxxxxxx>
wrote:
On Thu, 25 Mar 2010 07:07:50 -0700 (PDT), James Harris wrote:
...
If "binop" is a binary operator (in the sense of taking two
parameters), whether a symbol or a name as in

result := data1 binop data2

the programmer needs to be able to find which binop is being referred
to. Some options:

1. Use the type of data1.
2. Use the types of both data1 and data2.
3. Use the types of data1 and data2 and the type of result.
4. Require data1 and data2 to be of the same type and use that binop.
...
The only consistent scheme in presence of derived types is 3. Consider real
derived from complex. The operation + is covariant in both arguments and
the result, i.e.

+ : Real x Real -> Real
+ : Complex x Complex -> Complex

Sorry, Dmitry, but I sometimes find myself lost by your arguments -
and this is no exception. Can you put what you were saying in simpler,

I'll try...

For example, I'm puzzled by the suggestion that
Real might be derived from Complex.

Derive Complex from Real, that does not matter. The point is that in the
end you will have at least these two valid combinations overloaded:

real + real -> real
complex + complex -> complex

Then depending on what is derived from what and in which way you might have
other desirable combinations:

real + complex -> complex
complex + real -> complex

and also undesirable ones:

real + complex -> real
etc.

If you wanted to handle this properly you will need to support #3 plus some
dominance rules (as in C++) plus multiple dispatch if real and complex are
related types.

And what is to be learned from
your binops where both input types are the same?

Hmm, I don't see any problems with that. Can you explain it a bit.

It is also the most readable scheme, because humans do not parse text
left-to-right, bottom-up. They start in the middle jumping here and there.

I think option 3 has merit but it only applies where the type of the
result is known. For

(a binop b)

within an expression the result type is not defined.

Look, if you declare it "bottom-up" it is. But the premise is wrong,
normally, in a language, you don't have freely hanging expressions, of
which target type is totally unknown. An expression appears either as an
argument of a call, or as an initialization, or as the right side of an
assignment. In all these cases the target type is known. I put aside the
issue of type inference or duck types, because I don't like them. Otherwise
you always have an expected type to match. Of course it can happen that,
for example, the called operation is overloaded and you have many competing
alternatives. But each of the alternatives has strictly one expected type.
Then is just a case for the overloading resolution rules to choose one, or
else to report either "nothing matched" or "ambiguous expression, competing
candidates are ..."

explicit but that would make the source rather ugly.

No, it is not ugly. For example, the scheme #3 is deployed in Ada. It works
great. In 95% cases the overloading can be resolved with no problems. The
rest 5% are compile errors - "ambiguous expression." In these cases you
have to disambiguate the expression. It can be done in two ways:

1. Fully qualified naming of the operation, e.g. Standard."+" instead of +.
Here Standard is the package name where + was declared. In C++ you use
Type::operator for the same purpose.

2. Qualified expression, e.g. Integer'(1) instead of 1. Here Integer'
indicates the type of 1. (In C++ there is no equivalent, because C++ is
strictly bottom-up).

And note Ada does not have dominance rules, like C++ has. The "bottom-up"
C++ resolution rules are far more complex and non-trivial than Ada's ones.
The point is that "bottom-up" does not make things simpler, as one might
think.

--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
.