operator overloading

O

Owen Jacobson

You need to be able to have things which you can, say, add and multiply,
but not divide - anything a mathematician would call a ring, like a
three-by-three matrix. Forcing these to have divide methods that just
threw an exception would be pretty poor.

You could even model a little hierarchy on discrete mathematics: Addable,
Multipliable, Ring extends Addable, Multipliable, Dividable, Field extends
Ring, Dividable, etc.

The question is, at the language and standard library level, do you
provide four interfaces (for +, *, /, and -) or one (for all four) or
five (both) or some other combination? If you provide an interface
for each operator, obviously applications can define their own
composite interfaces, but there may be places where the standard
library uses multiple operators on a single type, so there might end
up being some "standard" composite interfaces while other combinations
of operators are unrepresented. If you include every possible
combination of operators in the standard library, you end up with a
huge number of mostly-useless interfaces.

It's a hard question.

(And that doesn't even get into operators like ++ and +=; I'd probably
prefer to have +=, at least, implemented automatically in terms of +
instead of being its own operator, but it's not a clear choice.)
There are also some distinctly non-weird non-edge cases where this is
true, and where the operands are not the same type, and where you want to
be able to overload the operators according to operand type. An obvious
example would be a Vector3D; i want to be able to add two vectors to make
a third, but also to multiply a vector by a double to get a vector, and a
vector by a vector to get their dot product, which is a double. If i also
have Matrix3D (being a 3 x 3 matrix, to represent linear transformations),
i want to be able to add matrices, to multiply two to make a third
(although i have no idea what the geometric significance of that is!), to
multiply a matrix by a vector to make a transformed vector, etc.

I can't see how you'd do this with interfaces unless they were defined as
taking Object and returning Object, which would be a bit lame.

As others have already suggested, a solution like

public interface Addable<A, S> {
public S add (A addend);
}

with generic "S"um and "A"ddend types makes the most sense. It's
just, like so much in Java, unpleasantly verbose in the simple case --
a number-like class (for example, BigDecimal) would become

public class BigDecimal implements Addable<BigDecimal, BigDecimal>,
Comparable<BigDecimal>, ...;

That's a lot of "BigDecimal"s. And since BD is not final, most of
those generic types might have to be ? extends BigDecimal.
Like how equals and compareTo work.

Comparable is a generic interface (and under this scheme I'd recommend
just using it as-is to implement >, <, <=, and >=), so its compareTo
method takes "the right type".

Actually, that highlights another issue -- what to do with comparison
operators? == has a well-understood existing meaning (identity
comparison) that would be *very* expensive to change, as do = and !=;
===, :=, !==, =!, and <> are all ugly options.

-o
 
T

Tom Anderson

But, when one goes to night clubs asking young women:

You apparently find some very ugly young women.

But yeah, chicks be *insane* for functional programming. When they know
you can take them to a higher order without mutating their state, they're
all over you like warts on a Redmond C program.

tom

--
For the first few years I ate lunch with he mathematicians. I soon found
that they were more interested in fun and games than in serious work,
so I shifted to eating with the physics table. There I stayed for a
number of years until the Nobel Prize, promotions, and offers from
other companies, removed most of the interesting people. So I shifted
to the corresponding chemistry table where I had a friend. At first I
asked what were the important problems in chemistry, then what important
problems they were working on, or problems that might lead to important
results. One day I asked, "if what they were working on was not important,
and was not likely to lead to important things, they why were they working
on them?" After that I had to eat with the engineers! -- R. W. Hamming
 
T

Tom Anderson

Usually when the matter comes up, there is a quite a squawk of "over
my dead bodies".

My view is user defined operators with new names/symbols would be
acceptable, but overloading ordinary >> to do I/O is as C++ is an
abomination. Unicode has lots of symbols you could use as user-defined
operators.

As Patricia pointed out, this doesn't help the rather major case of
wanting to do maths on compound types with conventional operators.

I said it before and i'll say it again: the crimes committed in C++
reflect cultural, not technical, problem. Other languages let you overload
<< and >>, amongst other things, and haven't seen any abuse at all.

tom

--
For the first few years I ate lunch with he mathematicians. I soon found
that they were more interested in fun and games than in serious work,
so I shifted to eating with the physics table. There I stayed for a
number of years until the Nobel Prize, promotions, and offers from
other companies, removed most of the interesting people. So I shifted
to the corresponding chemistry table where I had a friend. At first I
asked what were the important problems in chemistry, then what important
problems they were working on, or problems that might lead to important
results. One day I asked, "if what they were working on was not important,
and was not likely to lead to important things, they why were they working
on them?" After that I had to eat with the engineers! -- R. W. Hamming
 
T

Tom Anderson

Bloch (I think it was him) had a presentation a while back on why
closures and Java don't mix; I myself was irrevocably convinced that
they were a poor idea when I saw that "return" and "return;" would mean
different things under the BGGA proposal (and then some people arguing
that the differences were "obvious").

Interesting. I need to look this up - Josh Bloch is a smart guy. I'm not
familiar with the BGGA proposal, so i should read up on that too.
Java's a great object-oriented language; don't try to force it to become
a functional one as well.

I simply don't buy that the two are non-orthogonal.
As Bloch points out in his presentation, there is a high risk. It mixes
with generics and autoboxing in such a way that some things that seem
like they should work just plain don't and can't. There is also the
great chance that you end up with a situation not unlike what happened
with generics: it goes only halfway and you end up with some aggravating
problems.

I really do need to read up on this!

The only counterargument i can offer is that there are plenty of other
languages out there that very successfully mix object-orientation and
closures etc. Saying that it wouldn't work in Java is special pleading,
and i don't buy it.

I'm not saying that the BGGA proposal isn't duff. But there's a difference
between one proposal being duff and the whole idea being duff.

tom

--
For the first few years I ate lunch with he mathematicians. I soon found
that they were more interested in fun and games than in serious work,
so I shifted to eating with the physics table. There I stayed for a
number of years until the Nobel Prize, promotions, and offers from
other companies, removed most of the interesting people. So I shifted
to the corresponding chemistry table where I had a friend. At first I
asked what were the important problems in chemistry, then what important
problems they were working on, or problems that might lead to important
results. One day I asked, "if what they were working on was not important,
and was not likely to lead to important things, they why were they working
on them?" After that I had to eat with the engineers! -- R. W. Hamming
 
T

Tom Anderson

The question is, at the language and standard library level, do you
provide four interfaces (for +, *, /, and -) or one (for all four) or
five (both) or some other combination? If you provide an interface for
each operator, obviously applications can define their own composite
interfaces, but there may be places where the standard library uses
multiple operators on a single type, so there might end up being some
"standard" composite interfaces while other combinations of operators
are unrepresented.

Are there really things which can be added and divided but not multiplied?
How often will it be a problem that there isn't an interface for this?
If you include every possible combination of operators in the standard
library, you end up with a huge number of mostly-useless interfaces.

Which is a pretty fair description of the standard library at present!
(And that doesn't even get into operators like ++ and +=; I'd probably
prefer to have +=, at least, implemented automatically in terms of +
instead of being its own operator, but it's not a clear choice.)

There are times when being able to override += would be useful, because
using + to synthesise it involves throwing away an object, and if they're
heavyweight, that's not great. If your objects are million-by-million
matrices, for instance, you'd really like to be able to add in place.

One thing we definitely shouldn't have is overriding of the = operator,
as C++ lets you do. That's just insane. Ditto the . operator.

"Yes, but you can do some really clever things with those!", the C++
brains cry. Yeah, and i'll do something really clever with *your face* if
you try it, says i.
As others have already suggested, a solution like

public interface Addable<A, S> {
public S add (A addend);
}

with generic "S"um and "A"ddend types makes the most sense. It's
just, like so much in Java, unpleasantly verbose in the simple case --
a number-like class (for example, BigDecimal) would become

public class BigDecimal implements Addable<BigDecimal, BigDecimal>,
Comparable<BigDecimal>, ...;

That's a lot of "BigDecimal"s.

A bit of syntactic sugar which let 'this' stand for the current declaring
class might help.
And since BD is not final, most of those generic types might have to be
? extends BigDecimal.

Urgh, is that how generics work? I'm not very au fait with them.

Also, can i declare:

public class Vector implements Multipliable<double, Vector>,
[QUOTE="Multipliable said:
Like how equals and compareTo work.

Comparable is a generic interface (and under this scheme I'd recommend
just using it as-is to implement >, <, <=, and >=), so its compareTo
method takes "the right type".

Actually, that highlights another issue -- what to do with comparison
operators? == has a well-understood existing meaning (identity
comparison) that would be *very* expensive to change, as do = and !=;
===, :=, !==, =!, and <> are all ugly options.[/QUOTE]

I don't see what's so bad about === and !==. In an ideal world, where we
had operator overloading from the start, ==/!= would be the ones that punt
to equals(), and ===/!== would be the ones that do identity. Like in pyhon
:).

tom

--
For the first few years I ate lunch with he mathematicians. I soon found
that they were more interested in fun and games than in serious work,
so I shifted to eating with the physics table. There I stayed for a
number of years until the Nobel Prize, promotions, and offers from
other companies, removed most of the interesting people. So I shifted
to the corresponding chemistry table where I had a friend. At first I
asked what were the important problems in chemistry, then what important
problems they were working on, or problems that might lead to important
results. One day I asked, "if what they were working on was not important,
and was not likely to lead to important things, they why were they working
on them?" After that I had to eat with the engineers! -- R. W. Hamming
 
T

Tom Anderson

I have not made these photographs, but was not able to
find the page containing the name of the photographer.

I realise that, i apologise if i gave the impression otherwise. I meant
'you' in the sense of 'one'.

tom
 
O

Owen Jacobson

It's worth looking at the way Haskell handles operator overloading and
numeric types here. There is a class of types, Num, which represent
"numbers": integers, reals, rationals, complex numbers, oddities like
quaternions, presumably the tensor family, whatever. Num provides +,
-, *, negate (used for unary -), and abs functions, and inherits
equality testing from the Eq class.

Num has a subclass, Real, which also subclasses Haskell's Ord class,
which in turn provides comparison operators. This is the root of most
the more common numeric types, not Num.

Real has two subclasses: Integral and Fractional. Both provide the /
operation; the difference is in the result type. Integral in turn has
two subclasses: Integer, which are bigints, and Int, which are
intended to be machine ints. Dividing two Integral values can only
produce another Integral; Integral also provides remainder and mod
operations.

Fractional's standard instances are the Float and Double types, which
map to what you'd expect from other languages. Fractional specifies
a / operator as well as a few other useful things.

Due to the way type classes work in Haskell it's extremely easy to add
new numeric types at arbitrary points in this tree and have them work
"as expected" with existing code. Your hypothetical Giant Matrix type
would likely be an instance of Num, and would add some operators of
its own to allow for multiplication by non-Giant Matrix values like
Reals.
Which is a pretty fair description of the standard library at present!

You've a call from the org.omg package complaining about unfair
representation. ;)

Aside from the "obvious" cases like CORBA support, there is very
little in the Java SE library that I would opt to remove. The XML
support could use some streamlining, perhaps; it's gotten overgrown as
Sun has gotten in the habit of declaring the XML library of the week
to be "standard" and adding it. So I suspect we disagree there, or
are thinking of different definitions of "standard library" (and if
you start including Java EE or other javax.* packages that aren't part
of the Java SE library, I'd certainly agree with you).

Library design, as ever, is a series of rather hard tradeoffs. :)
There are times when being able to override += would be useful, because
using + to synthesise it involves throwing away an object, and if they're
heavyweight, that's not great. If your objects are million-by-million
matrices, for instance, you'd really like to be able to add in place.

If your objects are million-by-million matrices, I would hope that you
have some kind of sparse matrix structure, or some other compositional
representation that doesn't actually hold all trillion elements in
memory simultaneously. Large isn't *necessarily* slow, if you're
clever.
One thing we definitely shouldn't have is overriding of the = operator,
as C++ lets you do. That's just insane. Ditto the . operator.

No argument from me. The . and = operators only make sense in C++
because C++ has objects-as-values. I'd add -> to the list (even
though the Java equivalent is .), since object indirection is a fairly
core part of the language and I don't want anyone monkeying with it.
A bit of syntactic sugar which let 'this' stand for the current declaring
class might help.


Urgh, is that how generics work? I'm not very au fait with them.

It Depends On What You Want. If, eg., BigDecimal implements
Comparable<BigDecimal> (which it does), and you subclass BigDecimal as
MyBigDecimal (which you can), then you wind up with MyBigDecimal
implementing Comparable<BigDecimal>, which is actually what you want
(as it allows clients to compare objects of your subclass to arbitrary
BigDecimal objects).

I hadn't thought through the implications completely when I wrote
that, hence the "might"; now that I have time to sit and play with it
Also, can i declare:

public class Vector implements Multipliable<double, Vector>,
Multipliable<Vector, double>, Multipliable<Matrix, Vector>

?

No, as double is not a reference type; no, as you can't implement an
interface more than once and all those generic specializations are
really the same interface. The former can be worked around with the
language as-is; the latter cannot. Allowing that would involve
changing the way method overloads are resolved, or adding rules to
forbid implementing both Multipliable<Double, Vector> and
Multipliable<Vector, Vector> - as it stands, you can't provide both
public Double multiply (Vector) and public Vector multiply (Vector) in
the same class, because the only distinction is the return type.

Jesus H! McQuary limit, anyone?
 
J

josh

The most useful thing Sun could do would be at least to overload the  
arithmetic operators to work with BigDecimal and BigInteger.  I believe it  
is this that has been proposed for Java 7, rather than general-purpose,  
user-defined operator overloading.

Alex Miller maintains a page detailing the current status and latest news  
surrounding the various Java 7 proposals:

http://tech.puredanger.com/java7/

Dan.

Thanks for the answer I simply would know news about proposal.

However, in my opinion, I think that a language must offer many
possiblilty
to do thinks and must be much more rich of features...

Java I think should implements advanced constructions (closures,
operator overloading, lambda expression, pointer functions and so on)
and after a programmer can choose if using or not.

Is not goog having few features.

Java is a clean and an advanced language but in this last years it's
loosing in flexibility
against, for examples, C# that is at the moment better that it.

In fact it seems that Java is running to reach it. In past it was the
opposite!!!!

At the end, yes I know, we can always to use the immortal C++...but
why?

I want to use Java and I want that the developers offer me as many
features as the programmers want so they can be more productive.

Regards
 
N

Nigel Wade

Stefan said:
Supersedes: <[email protected]>



From a person who is not a native speaker of English:
Is this pronounced »T plus T two«, »T add T two«,
or in yet another way?

The former, T plus T2.
If the previous sum was not pronounced using »add«,
why is »add« often used in expressions like

t.add( t2 )

instead of the word pronounced for the operator above?

"plus" is the common name of the operator symbol, the operation is actually
addition and the verb is "to add". The method is named (more correctly) after
the verb rather than the common name of the operator representing the
operation. However, in common usage we say "plus" and the code would read more
easily if the method were named "plus" [provided you knew that "plus" stood for
addition].

The same also applies to "minus" (subtraction) and "times" (multiplication).
Division is the odd one out.
 
T

Tom Anderson

It's worth looking at the way Haskell handles operator overloading and
numeric types here. [SNIP]

Thanks for that. It sounds a bit like Smalltalk's hierarchy, only a bit
more rigorously thought out.
You've a call from the org.omg package complaining about unfair
representation. ;)

Aside from the "obvious" cases like CORBA support, there is very little
in the Java SE library that I would opt to remove. The XML support
could use some streamlining, perhaps; it's gotten overgrown as Sun has
gotten in the habit of declaring the XML library of the week to be
"standard" and adding it. So I suspect we disagree there, or are
thinking of different definitions of "standard library" (and if you
start including Java EE or other javax.* packages that aren't part of
the Java SE library, I'd certainly agree with you).

Much or most of the standard library is indeed pretty good. There are
patches where it's not, though - J2EE, XML, Swing. I often feel like i'm
jumping through hoops with java.io, but only when trying to mix
readers/writers and streams.
If your objects are million-by-million matrices, I would hope that you
have some kind of sparse matrix structure, or some other compositional
representation that doesn't actually hold all trillion elements in
memory simultaneously.

No, i'm doing weather simulations. Or handling terapixel images.
No argument from me. The . and = operators only make sense in C++
because C++ has objects-as-values.

True. There are certainly positive things you can do with this power, but
it's still, IMHO, too much of a headache.
I'd add -> to the list (even though the Java equivalent is .), since
object indirection is a fairly core part of the language and I don't
want anyone monkeying with it.

It's that meaning of . i meant - java's ., which is C++'s ->. Java doesn't
really have a .. But if it did, i would want it to be overridable!

That said, python lets you override ., and it can actually be very useful
at times (making proxy objects and things like that). Without python's
dynamicity, it would be less useful, so i'm happy to leave it out of java.
No, as double is not a reference type; no, as you can't implement an
interface more than once and all those generic specializations are
really the same interface. The former can be worked around with the
language as-is; the latter cannot.

In that case, i think any interface-based solution fails.
Allowing that would involve changing the way method overloads are
resolved, or adding rules to forbid implementing both
Multipliable<Double, Vector> and Multipliable<Vector, Vector> - as it
stands, you can't provide both public Double multiply (Vector) and
public Vector multiply (Vector) in the same class, because the only
distinction is the return type.

That's fair enough. I can't think of any approach to operator overloading
that would allow that without further language change. Overloading on
return type is the kind of thing perl would do.
Jesus H! McQuary limit, anyone?

Apologies! I mostly have 1-3 line sigs, but i love that quote, and i
couldn't see any way to trim it down. So, once in a while, i indulge
myself and impose it on usenet. On average, my sig length is still well
under the limit, and probably shorter than most (most who have sigs, that
is).

tom
 
T

Thomas Schodt

josh said:
Java I think should implements advanced constructions (closures,
operator overloading, lambda expression, pointer functions and so on)
and after a programmer can choose if using or not.

Is not good having few features.

You are missing one major point.

Actually writing the code is not a big chunk of
the man power that goes into an application.

Adding new stuff to an already written application
(often written by someone else)
as well as support
can be much more resource consuming.

Many of these advanced features you want to use
can make these things much more expensive.
 
T

Tom Anderson

You are missing one major point.

Actually writing the code is not a big chunk of the man power that goes
into an application.

Adding new stuff to an already written application (often written by
someone else) as well as support can be much more resource consuming.

Many of these advanced features you want to use can make these things
much more expensive.

Overloaded operators are not hard to understand. They're just methods with
unusual names. There is simply nothing strange about them.

tom
 
J

Joshua Cranmer

Owen said:
No, as double is not a reference type; no, as you can't implement an
interface more than once and all those generic specializations are
really the same interface. The former can be worked around with the
language as-is; the latter cannot. Allowing that would involve
changing the way method overloads are resolved, or adding rules to
forbid implementing both Multipliable<Double, Vector> and
Multipliable<Vector, Vector> - as it stands, you can't provide both
public Double multiply (Vector) and public Vector multiply (Vector) in
the same class, because the only distinction is the return type.

Multipliable<Double, Vector> would essentially provide the same service
as Multipliable<double, Vector> thanks to auto-(un)boxing.

Also (as the one who posted with this proposal), the idea of LHS
operator overloading cannot be satisfactorily be solved in this manner;
I've been racking my brains for a solution and have found known (more
discussion in my earlier thread 'Java 7 Features').

This would be Tom's proposed declaration (I believe he munged the
syntax), then:

public class Vector implements Multipliable<Double, Vector> {
}

public class Matrix implements Multipliable<Double, Matrix>,
Multipliable<Matrix, Matrix>, Multipliable<Vector, Vector> {
}

Unfortunately, to do so would still require modification such that a
class can implement the same generic interface multiple times with
different generic arguments--i.e., reification of generics.
 
T

Tom Anderson

Those are three areas where Java libraries really shine, actually.
No.


java.io is pretty straightforward. What troubles does it cause you?

Normally one would want to avoid mixing Readers/Writers with Streams.
The secret to getting them to work together is encoding. One can hardly
blame the Java API for dealing with such a complex matter and (oh,
horror) letting some of that complexity be visible to the programmer.

I'm not convinced it does it in the best way possible.

I think i was doing something where i needed to be able to read lines of
text, but also things in binary, from a stream. To read lines, you need a
BufferedReader, and to read binary data, you need a DataInputStream; you
can wrap the BufferedReader round an InputStreamReader, and wrap that
round the DataInputStream, or whatever stream underlies that. But then you
have a problem, because the BufferedReader is eating more the data than it
should. DataInputStream has a readLine of its own, but it'd deprecated and
doesn't do character encoding.

More generally, i just don't see why there needs to be a Reader/Writer
hierarchy that parallels the InputStream/OutputStream hierarchy. Why not
just have a Reader, which wraps an InputStream and does decoding, and a
Writer, which wraps an OutputStream and does encoding? The Reader would be
able to read lines, but only if on top of a PushbackInputStream. That
would have solved my problem in a trice.

I guess there's a case for StringReader/StringWriter too. But not the
File/Buffered/whatever ones. Byte streams can provide those facilities.

tom
 
J

John W Kennedy

Tom said:
I think i was doing something where i needed to be able to read lines of
text, but also things in binary, from a stream. To read lines, you need
a BufferedReader, and to read binary data, you need a DataInputStream;
you can wrap the BufferedReader round an InputStreamReader, and wrap
that round the DataInputStream, or whatever stream underlies that. But
then you have a problem, because the BufferedReader is eating more the
data than it should. DataInputStream has a readLine of its own, but it'd
deprecated and doesn't do character encoding.

More generally, i just don't see why there needs to be a Reader/Writer
hierarchy that parallels the InputStream/OutputStream hierarchy. Why not
just have a Reader, which wraps an InputStream and does decoding, and a
Writer, which wraps an OutputStream and does encoding? The Reader would
be able to read lines, but only if on top of a PushbackInputStream. That
would have solved my problem in a trice.

I guess there's a case for StringReader/StringWriter too. But not the
File/Buffered/whatever ones. Byte streams can provide those facilities.

The problem is that what you're asking for covers only a handful of
cases: those in which binary data gets mixed with LF-terminated (or CRLF
or CR) string data. It does not cover cases where the string data is
embedded in the binary data, which is far more common in the real world.
The normal way to handle it is:

final DataInputStream dis = new DataInputStream(...);
final CharSet cs = CharSet.forName("ISO-8859-1");
final byte[] ba = new byte[32];
...
for (;;) {
...
dis.readFully(ba);
final String st = new String(ba, cs);
...
}

or, for variable-length strings, probably something like:

for (;;) {
...
final short l = dis.readShort();
final byte[] ba = new byte[l];
dis.readFully(ba);
final String st = new String(ba, cs);
...
}



(Exception: if your file is /only/ to be read and written by Java, the
WriteUTF and ReadUTF may be appropriate.)
--
John W. Kennedy
"You can, if you wish, class all science-fiction together; but it is
about as perceptive as classing the works of Ballantyne, Conrad and W.
W. Jacobs together as the 'sea-story' and then criticizing _that_."
-- C. S. Lewis. "An Experiment in Criticism"
 
M

Mark Space

Owen said:
It might be instructive to think about how foreach was made
extensible: there is an interface which, if implemented properly, can
make an arbitrary class usable in a for(each) loop. The same could be
done to map syntax to operations:
However, it's not clear whether the core operators (+, *, /, binary -,
unary -, ++, --) should be exposed as a single interface (Arithmetic),
several interfaces (Addable, Multipliable, Dividable/Rational, etc).

I wonder too if it might be better to allow *new* operators, rather than
change existing ones.

class Matrix {

public Matrix add( Matrix m )
{
//...
}
//...
}

has to be invoked like this:

Matrix x = y.add( z );

but what if you could call it like this:

Matrix x = y .add. z;

?

Just allow a more algebraic notation with method names as a convenience
for those who need it.

Matrix a = ( .negate. s ) .multiply. z;

Hmm, unary negation bothers me... nothing on the left side... maybe it
would be assumed that s has a parameterless method with the same name in
that case? Same as

Matrix a = ( s.negate() ).multiply( z );

I'm not sure, I'm not one of the advocates for operator overloading in
Java....
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,772
Messages
2,569,591
Members
45,103
Latest member
VinaykumarnNevatia
Top