August 17, 2001
>
> For numerical computing it is convenient to define classes e.g. vectors, matrices and other entities beyond complex numbers, such as quaternions.  For this overloading of operators such as +, -, +=, etc means that top level code can be easily written and readable.

Operator overloading as it exists in C++ is unsatisfactory.  Unless it can be radically improved I don't see the point of having the feature.

I do a lot of graphics programming (for fun, I program Telecommunications software for a living), so I use Vectors a lot.  Personally I think they provide a good reason why you *don't* want to use operator overloading. Yes, you can overload +, -, +=, and you can overload * for cross-product, but you can't overload . (period) for dot product.  The problems with operator overloading in C++ are:

(1) You can only overload the operators that C++ already provides, which deminishes the value of operator overloading, and is a damn pain when there are some extremely common operations for your type which would benefit from it (like a dot operator)

(2) You inheirit the precedence of the native operators from C++ which is
not always appropriate.

(3) Overloading new is just *evil*.  It makes debugging a large codebase
code a nightmare.

(4) It introduces another style for doing very common stuff.  One group of programmers does vec.plus(v2) and another does vec + v2.  One of the main issues with maintaining any large code base is uniformity of style. The more you can encourage people to express simple concepts in the same way, the better.

Personally I think that overloading is not worth the hype and tends to obfuscate code.  If you *must* implement it, don't do it unless you can use any operators you like (regardless of whether the language supports them natively) and  you must be able to specify your own precedence (that'd be a fun compiler to write!).


Peter.


August 17, 2001

kaffiene wrote:

>
> Personally I think that overloading is not worth the hype and tends to obfuscate code.  If you *must* implement it, don't do it unless you can use any operators you like (regardless of whether the language supports them natively) and  you must be able to specify your own precedence (that'd be a fun compiler to write!).
>
> Peter.

I know of a C++ compiler (SALFORD C++ http://www.suns.salford.ac.uk/compilers/salfordc/index.shtml) which has a pragma to allow the definition of new operators and specify the position in the precedence.  It also produces very lint like error messages. I could not find a user group.

John Fletcher

August 17, 2001
For LX, I invented the "written" notation. In D-like syntax, it would be:

    matrix Add(matrix M, matrix N) written M+N
    {
        ...
    }

One benefit is that it allows you to define N-way operators:

    matrix MultiplyAndAdd(matrix A, float B, matrix C) written A*B+C
    {
        ...
    }

There are many cases where this is really simpler or more efficient than two-way operators. Another slight extension offered by LX is to allow the definition of named infix operators (which all share the same, lowest priority):

    int And(int A, int B) written A and B;


This is much easier to define and implement than it sounds. This being presented, I can discuss your e-mail in more details...


kaffiene wrote:

> >
> > For numerical computing it is convenient to define classes e.g. vectors, matrices and other entities beyond complex numbers, such as quaternions.  For this overloading of operators such as +, -, +=, etc means that top level code can be easily written and readable.
>
> Operator overloading as it exists in C++ is unsatisfactory.  Unless it can be radically improved I don't see the point of having the feature.

The point being that a very large portion of C++ code out there uses it in one form or another. I take this as an indication that it's probably useful to some. Avoid the common pitfall of thinking: "I don't need it, therefore nobody needs it."


>
> I do a lot of graphics programming (for fun, I program Telecommunications software for a living), so I use Vectors a lot.  Personally I think they provide a good reason why you *don't* want to use operator overloading. Yes, you can overload +, -, +=, and you can overload * for cross-product, but you can't overload . (period) for dot product.

WIth the written approach, at least, you could define

    float DotProduct(vector A, vector B) written A dot B

(As a matter of fact, the LX compiler would let you override A.B that way, if memory serves me right, but it might not be possible in the general framework of the D semantics)


>  The problems with
> operator overloading in C++ are:
>
> (1) You can only overload the operators that C++ already provides, which deminishes the value of operator overloading, and is a damn pain when there are some extremely common operations for your type which would benefit from it (like a dot operator)

As a matter of fact, overloading the . operator was a hot debate within the C++ committee. Microsoft was all in favor of it. I personally consider a mistake that it was not allowed, because it disabled a whole class of "smart objects".

Anyway, this doesn't have to be the case in D.

> (2) You inheirit the precedence of the native operators from C++ which is
> not always appropriate.

That's not a problem, that's a feature. The compiler would be unable to parse expressions correctly if operator precedence changed. If the compiler can't parse it, the human brain probably would have trouble too. For instance, say you have A+B*C, with operators that make both (A+B)*C and A+(B*C) be valid, with different meanings... what do you select? If you enable variable-precedence operators, you end up with a real compile-time nightmare, at best.

This being said, the written approach allows you to redefine priorities, if you are really nasty.

    glop Reorder(glop A, glop B, glop C) written A+B*C
    {
        return (A+B)*C; // Yuck yuck
    }


>
> (3) Overloading new is just *evil*.  It makes debugging a large codebase
> code a nightmare.

The LX compiler uses a form of garbage collection. The first thing it needs to do is to know what objects to collect. operator new tells me precisely that.

The problem with C++ features in general is not that they are necessarily bad, but that people want to show off by using them at the wrong place and time. There are C++ features that are bad (declaration syntax, lookup rules). But definitely not operator overloading.

>
>
> (4) It introduces another style for doing very common stuff.  One group of programmers does vec.plus(v2) and another does vec + v2.  One of the main issues with maintaining any large code base is uniformity of style. The more you can encourage people to express simple concepts in the same way, the better.

The absence of operator overloading is bad, because it introduces yet another
style for doing very common stuff. For instance, to add two integers, I write
A+B, but to add two vectors, I write A.add(B). One of the main issues with
maintaining any large code base is uniformity of style. The more
you can encourage people to express simple concepts in the same way, the
better.

Actually, this is more serious than you think. You just CAN'T WRITE TEMPLATE CODE if the form in which you express things is not common.


> Personally I think that overloading is not worth the hype and tends to obfuscate code.  If you *must* implement it, don't do it unless you can use any operators you like (regardless of whether the language supports them natively) and  you must be able to specify your own precedence (that'd be a fun compiler to write!).

Personally, I think that overloading tends to less obfuscated code than most other notations.


Christophe

August 17, 2001
Carlo E. Prelz wrote:
> Jonathan Cano wrote:
> ...
>>
>
> I think the problem here is to be able to balance the complexity you are
> inserting with the amount of real-life problems you are solving.
> Operator overloading implementation as a problem has been solved many
> times already.
>
> I personally agree with Walter's decision: operators mean something very
> precise for me. For me, adding fancy meanings to them would just add to
> the confusion of the result.
>
> Carlo
>
>
Overloaded operators can be quite useful, but perhaps the standard
operators shouldn't be overloadable.  Perhaps one should instead be able
to define infix functions (i.e., operators) have a form like, O, just as
a wild choice /\:[-~!@#$%^&*_+`,?0-9A-z]*\:/, i.e., no white space, no
colons, no control characters, no parens, bracketts, or braces, no backslash, and surrounded by colons.  Also require white-space separation from everything else.  So one could define :+: to add matrices, etc., and as these would really be functions, they should follow the normal overloading rules of functions.

This would allow them to be easily parsed, would distinguish them
clearly from the standard operators, and would provide the majority of
the notational compactness that normal operators provide.  The fact that
A :+: B would be syntactic sugar for A.add (B) is minor, but convenient.
  As to precedence ... they should probably bind more strongly than any
other operator, and all to the same degree.  If you want to get fancy,
you'ld need to use parentheses.


August 17, 2001
If the C++ overloading doesn't work well, then look at other languages.  This is one thing that Eiffel handles fairly well (though I was at one point ungruntled after it refused to allow = [i.e., test for equality] to be overridden).


August 17, 2001
Charles Hixson wrote in message <3B7D2C6B.4040702@earthlink.net>...
>Overloaded operators can be quite useful, but perhaps the standard operators shouldn't be overloadable.  Perhaps one should instead be able to define infix functions (i.e., operators) have a form like, O, just as a wild choice /\:[-~!@#$%^&*_+`,?0-9A-z]*\:/, i.e., no white space, no colons, no control characters, no parens, bracketts, or braces, no backslash, and surrounded by colons.  Also require white-space separation from everything else.  So one could define :+: to add matrices, etc., and as these would really be functions, they should follow the normal overloading rules of functions.
>
>This would allow them to be easily parsed, would distinguish them clearly from the standard operators, and would provide the majority of the notational compactness that normal operators provide.  The fact that A :+: B would be syntactic sugar for A.add (B) is minor, but convenient.
>   As to precedence ... they should probably bind more strongly than any
>other operator, and all to the same degree.  If you want to get fancy, you'ld need to use parentheses.


Now this idea has a lot of merit! Thanks for posting it. I *like* it being clearly distinguishable from the native operators.


August 18, 2001
As someone who writes tons of vector math code in production applications, my feeling is that a language which doesn't support operator overloading and template-style programming is really going to be really painful for moden math-intensive applications, such as games, modelling programs, etc.

I'm not necessarily advocating C++'s approach (operator overloading can be confusing, such as using an overloaded "<<" operator to represent both bit shifting and character output!)

Haskell-style "typeclasses" are an interesting replacement for general operator overloading.  This approach encapsulates the idea that a given operator like "+" or "*" should have the same notional meaning everywhere, even when operating on different data types.

A simple example is that "+" and "*" would belong to the "numeric" typeclass.  If you create a new numeric typeclass of your own (for example, a "bigint" class), then you declare your class to belong to the "numeric" typeclass, and implement those specific operators.  However, you wouldn't be able to create a "+" operator for a non-numeric class.

This can map to popular languages easily by having your class implement an abstract interface (aka typeclass) and provide an appropriate static or friend function for those operators.

However, this approach doesn't extend well to more general situations.  For example, say you define a template like vector<float,4> (a mathematical 4-component vector), one wants the ability to multiply those vectors by scalars using "*", which requires the more general form of overloading.

-Tim

"John Fletcher" <J.P.Fletcher@aston.ac.uk> wrote in message news:3B78E154.BC62E006@aston.ac.uk...
> Quote from the specification for D:
>
> Operator overloading. The only practical applications for operator overloading seem to be implementing a complex floating point type, a string class, and smart pointers. D provides the first two natively, smart pointers are irrelevant in a garbage collected language.
>
> Another quote:
>
>  D has many features to directly support features needed by numerics
> programmers, like direct support for the complex data type and defined
> behavior for NaN's and infinities.
>
>
> Comment:
>
> For numerical computing it is convenient to define classes e.g. vectors, matrices and other entities beyond complex numbers, such as quaternions.  For this overloading of operators such as +, -, +=, etc means that top level code can be easily written and readable.
>
> John Fletcher
>
>


August 18, 2001
Walter wrote:
> 
> Charles Hixson wrote in message <3B7D2C6B.4040702@earthlink.net>...
> > ...So one could define :+: to add
> >matrices, etc., and as these would really be functions, they should follow the normal overloading rules of functions.
> >
> >This would allow them to be easily parsed, would distinguish them clearly from the standard operators, and would provide the majority of the notational compactness that normal operators provide.  The fact that A :+: B would be syntactic sugar for A.add (B) is minor, but convenient.
> >   As to precedence ... they should probably bind more strongly than any
> >other operator, and all to the same degree.  If you want to get fancy, you'ld need to use parentheses.
> 
> Now this idea has a lot of merit! Thanks for posting it. I *like* it being clearly distinguishable from the native operators.

Again, I think I read in Stroustrup the suggestion that a language that was happy using unicode as the source charset could allow you to overload "funny characters" as operators.

This would let you use, e.g., u22C5 ("dot operator") for matrix multiply, u22C5 and u00D7 ("multiplication sign") for dot and cross products of vectors, u221A, u221B, u221C for square, third, and fourth roots, etc.

This lets a huge variety of obscure branches of mathematics use
a "native notation", including notations that haven't been invented
yet. You do need a way of describing the prefix/postfix/infixity
and possibly precedence and binding of such new operators, though.[1]

Of course, adopting this rule leads you down a slippery slope back down to the plus sign. I'd be very happy to have you wind up at the bottom of that slippery slope and decide that C++-style operator overloading is acceptable for D. All language features are abusable; don't let the design of iostream sour you on the concept of operator overloading.

-Russell B

[1] I didn't like the "dummy operand" solution for ++/-- in C++, though. How about ++operator and operator++ ?
August 18, 2001
Russell Bornschlegel wrote in message <3B7DC29E.3415E464@estarcion.com>...
>Of course, adopting this rule leads you down a slippery slope back down to the plus sign. I'd be very happy to have you wind up at the bottom of that slippery slope and decide that C++-style operator overloading is acceptable for D. All language features are abusable; don't let the design of iostream sour you on the concept of operator overloading.


I admit that implementing iostream and just the way it *looks* turned me
off.
All those << and >> just rubs me the wrong way.

>[1] I didn't like the "dummy operand" solution for ++/-- in C++, though. How about ++operator and operator++ ?

Why not just say ++ and -- are not overloadable <g> ?


August 18, 2001
"Christophe de Dinechin" <descubes@earthlink.net> wrote in message news:3B7D2B88.C079FD16@earthlink.net...
> For LX, I invented the "written" notation. In D-like syntax, it would be:
>
>     matrix Add(matrix M, matrix N) written M+N
>     {
>         ...
>     }
>
> One benefit is that it allows you to define N-way operators:
>
>     matrix MultiplyAndAdd(matrix A, float B, matrix C) written A*B+C
>     {
>         ...
>     }
>
> There are many cases where this is really simpler or more efficient than two-way operators. Another slight extension offered by LX is to allow the definition of named infix operators (which all share the same, lowest priority):
>
>     int And(int A, int B) written A and B;
>
>
> This is much easier to define and implement than it sounds. This being presented, I can discuss your e-mail in more details...

That's really nice.
>
> > >
> > > For numerical computing it is convenient to define classes e.g.
vectors,
> > > matrices and other entities beyond complex numbers, such as quaternions.  For this overloading of operators such as +, -, +=, etc means that top level code can be easily written and readable.
> >
> > Operator overloading as it exists in C++ is unsatisfactory.  Unless it
can
> > be radically improved I don't see the point of having the feature.
>
> The point being that a very large portion of C++ code out there uses it in
one
> form or another. I take this as an indication that it's probably useful to some. Avoid the common pitfall of thinking: "I don't need it, therefore
nobody
> needs it."

That is not my point of view at all - my view is that one should avoid multiplying the number of ways in which you can achieve the same result - it makes for a code maintainence headache.  I think C++ is supremely guilty of this.

Perl is an example of a language which will provide a zillion solutions to a given problem.  It is also a language that you wouldn't want to maintain a large code base in.  I think these two facts are related.

>
> >
> > I do a lot of graphics programming (for fun, I program
Telecommunications
> > software for a living), so I use Vectors a lot.  Personally I think they provide a good reason why you *don't* want to use operator overloading. Yes, you can overload +, -, +=, and you can overload * for
cross-product,
> > but you can't overload . (period) for dot product.
>
> WIth the written approach, at least, you could define
>
>     float DotProduct(vector A, vector B) written A dot B

That really is nice.

> (As a matter of fact, the LX compiler would let you override A.B that way,
if
> memory serves me right, but it might not be possible in the general
framework
> of the D semantics)
>
>
> >  The problems with
> > operator overloading in C++ are:
> >
> > (1) You can only overload the operators that C++ already provides, which deminishes the value of operator overloading, and is a damn pain when
there
> > are some extremely common operations for your type which would benefit
from
> > it (like a dot operator)
>
> As a matter of fact, overloading the . operator was a hot debate within
the C++
> committee. Microsoft was all in favor of it. I personally consider a
mistake
> that it was not allowed, because it disabled a whole class of "smart
objects".
>
> Anyway, this doesn't have to be the case in D.
>
> > (2) You inheirit the precedence of the native operators from C++ which
is
> > not always appropriate.
>
> That's not a problem, that's a feature. The compiler would be unable to
parse
> expressions correctly if operator precedence changed. If the compiler
can't
> parse it, the human brain probably would have trouble too. For instance,
say
> you have A+B*C, with operators that make both (A+B)*C and A+(B*C) be
valid,
> with different meanings... what do you select? If you enable variable-precedence operators, you end up with a real compile-time
nightmare,
> at best.

The rhetoric of operator overloading is that it is better because it more accurately reflects the normal mathematical notation of operations in a given context.  If operator overloading must enforce a given precedence then this is not true, is it?  If I define a * operation on Vectors for cross-product, then I want it to have the same precedence as . for dot product.  And why should a vector cross product be arbitrarily forced to haver higher or lower precedence that other operators such as addition?  No such limitation is imposed on the mathematical notation.  Having to put up with it because its a hangover from how the compiler handles basic types doesn't seem like a very noble, clean or advanced notion, does it?

> This being said, the written approach allows you to redefine priorities,
if you
> are really nasty.
>
>     glop Reorder(glop A, glop B, glop C) written A+B*C
>     {
>         return (A+B)*C; // Yuck yuck
>     }
>
>
> >
> > (3) Overloading new is just *evil*.  It makes debugging a large codebase
> > code a nightmare.
>
> The LX compiler uses a form of garbage collection. The first thing it
needs to
> do is to know what objects to collect. operator new tells me precisely
that.
>
> The problem with C++ features in general is not that they are necessarily
bad,
> but that people want to show off by using them at the wrong place and
time.
> There are C++ features that are bad (declaration syntax, lookup rules).
But
> definitely not operator overloading.
>
> >
> >
> > (4) It introduces another style for doing very common stuff.  One group
of
> > programmers does vec.plus(v2) and another does vec + v2.  One of the
main
> > issues with maintaining any large code base is uniformity of style. The
more
> > you can encourage people to express simple concepts in the same way, the better.
>
> The absence of operator overloading is bad, because it introduces yet
another
> style for doing very common stuff. For instance, to add two integers, I
write
> A+B, but to add two vectors, I write A.add(B). One of the main issues with
> maintaining any large code base is uniformity of style. The more
> you can encourage people to express simple concepts in the same way, the
> better.

My point is that you have arithmetic operators for basic types. You have methods calls on objects.  This is two ways of doing things.  If you add operator overloading that's three.  With operator overloading you will still end up with some objects using method calls for add, subtract etc... and other code using operator overloading to achieve exactly the same kind of thing.

I *know* that the idea is to have object manipulations looking like operations on simple arithmetic types but the fact is that as soon as you work with more than about three programmers, or inheirit code from someone else or import an API, someone is going to to do the object manipulations in a different way to you.  Given that you are commited to having general method calls on objects (obj.method()) deciding to do *all* manipulations that way ensures that there is one and only one style for performing such operations.  Choosing to use operator overloading guarantees that as soon as you move beyond your own stylistically/ideologically pure code, some other bugger will mess it up by doing it a different way and you have no choice but to use their idiom.  Maintaining C++ code is full of experiences like this - *whichever* style you choose some other code that you have to work with does it radically different.

Operator overloading is typically implemented in a very limited way, so it often doesn't do the job of acurately representing the mathematical notation anyway.  For example, on vectors I often want to get the magnitude.  This is usually represented as ||v|| for magnitude of v.  Most operator overloading systems don't cope with such notation, and so cannot fully represent the syntax for working with a given mathematical type properly.  It seems to me that you can:

(a) Go the C++ route and say that only some predetermined operators may be overloaded and that you have to live with the precedence that they already have - this falls short of the stated goal of making the code syntax look like the mathematical syntax.

(b) Go the whole hog and design a system that allows creation of arbitrary operators and overloading the precedence of these and existant operators. This actually *would* allow your system to live up to the rhetoric, but you have to put up with the proliferation of style choices made by each individual programmer you work with, and live with the fact that that a maintenance programmer who is unfamiliar with the given mathematical notation will have an uphill battle to be able to work on your codebase.

(c) Don't overload operators at all and have just one idiom for calling
methods on objects.

I think that there are two extremes - (b) and (c).  I believe that (c) is
the best idea, but if you must overload operators at least do it properly
(b), but personally I think that the negatives (maintenence costs) far
outweigh the gains.  (a) is a system that does not really do anything well.

Peter.