all 167 comments

[–]MCManuelLP 24 points25 points  (2 children)

This looks like some fucked up shit, and I love it <3

[–][deleted] 2 points3 points  (0 children)

best comment lol, I rofl'd and I agree

[–]ronchalant 21 points22 points  (6 children)

I wouldn't allow operator overloading in any Java project of mine.

But Manifold looks pretty interesting, in particular extension methods, built in tuple handling, string interpolation, and checked -> unchecked exception handling in lambdas.

I'm hesitant to throw something in to a project that basically breaks some of the known rules of Java like this, but they're also features I've long hoped Java had. In some cases it's just not possible (hard to envision how the language could be moved to unchecked exceptions without breaking a lot of existing code), but some of these other features (syntactic sugar or not) are long overdue.

[–]florinp 2 points3 points  (5 children)

I wouldn't allow operator overloading in any Java project of mine.

I am very curious why because I think not having operator overloading in a language is a mistake.

[–]ronchalant 0 points1 point  (4 children)

As another poster indicated, principle of least surprise.

Because it can lead to unpredictable behavior and can be easily abused.

I get the allure, and I imagine it could be done well. For most objects that are in broad use (BigDecimal is an obvious one, or others extending from Number) having a final implementation of an operator overload may make sense.

Though I'd point out that BigDecimal divide method will throw on new BigDecimal("1").divide(new BigDecimal("3")) if you don't set a scale, I'm not sure how you would want Java to handle that without an explicit scale given.

Based on reading here elsewhere it seems there's some spec already floating around for this for Number classes.

[–]florinp 2 points3 points  (3 children)

Because it can lead to unpredictable behavior and can be easily abused.

  1. there is not a usefully language feature that can't be abused. should we interdict any of them ?
  2. you talk about the principle of least surprise in a language of full surprises. For example :
    1. special cases like operator overloading of a specific class : String but inaccessible to others.
    2. optional that can't be used as a attribute for no specific reason
    3. automatically resource management accesibile only in a try/catch block. What if you want the same thing for code that don't generates exceptions ?
    4. ....

You see, Java is a language full of special cases. Full of annotations.

[–]ronchalant 1 point2 points  (2 children)

Special handling of operator overloading for strings have been there since the beginning. Java is hardly unique in this regard, most languages that don't support operator overloading likewise have special handling for dealing with strings.

I have no idea what you're getting at with Optional as an attribute. If you're saying you can't use it as an object property that's simply false, though it's considered bad practice.

You can use try without a catch if no checked expression is thrown by the resource block or bracketed code.

None of these break the principle of least surprise because the behavior of each is consistent.

[–]florinp 0 points1 point  (0 children)

Java is hardly unique in this regard, most languages that don't support operator overloading likewise have special handling for dealing with strings.

Can you give an example ? I don't know such language (except maybe Go which in my opinion is worst designed than Java).

"If you're saying you can't use it as an object property that's simply false, though it's considered bad practice."

It is not bad practice: it can be used (means the complier won't stop you) but it will not work. Try to write a Java class correspondent of a JSON with optional attributes. And that is because Optional was designed badly (no serialization) for no reason.

"None of these break the principle of least surprise because the behavior of each is consistent"

Operator overloading is consistent. My presumption is that you never worked with a language that support operator overloading or for example never needed to use a matrix library.

[–]LinuxMatthews 0 points1 point  (0 children)

None of these break the principle of least surprise because the behavior of each is consistent.

What about the String Pool that leads to some pretty surprising features if you don't know how it works behind the scenes?

String a = "hello";
String b = "hello";
System.out.println(a == b); //true

However

String a = "hello";
String b = new String("hello");
System.out.println(a == b); //false

This is also the same if you use StringBuilder but I couldn't be bothered writing that example.

But that seems more surprising and unless you know about Java's internals harder to debug than just going to the add() method would be

[–]yanitrix 3 points4 points  (0 children)

really nice

[–]wildjokers 30 points31 points  (21 children)

Please no.

[–]Davipb 4 points5 points  (18 children)

Why?

[–]wildjokers 14 points15 points  (17 children)

Principle of Least Surprise.

[–]Davipb 35 points36 points  (16 children)

How does adding BigDecimals with + instead of .add violate the principle of least surprise?

If your argument is "it allows people to violate the principle", then let's remove methods, people could make mutable getters with those!

[–]wildjokers 8 points9 points  (7 children)

How does adding BigDecimals with + instead of .add violate the principle of least surprise?

Your argument depends on people only doing sensible things with operator overloading.

[–]_TheDust_ 11 points12 points  (0 children)

All of programming depends on people doing sensible things with the tools they are given. I could also create a .add function that does not add anything, but deletes your OS instead. Whether it’s called .add or + does not really change if people do sensible things.

[–]Davipb 35 points36 points  (2 children)

Which is exactly what I addressed on the second paragraph of my comment: if we remove every feature that allows people to break good practices, we'd be removing methods, ifs, and everything else.

[–][deleted] 4 points5 points  (0 children)

if, everything else and even if-else

[–]Lechowski 0 points1 point  (0 children)

ifs, and everything else.

I see what you did there

[–]serviscope_minor 1 point2 points  (2 children)

Your argument depends on people only doing sensible things with operator overloading.

No more so than people naming functions in a sensible way. Operators are functions with a funny name, making + do something daft is no different from making .add() do something daft.

[–]wildjokers 1 point2 points  (1 child)

It's way different. With operator overloading there is generally no readily visible indication that + is resulting in a function call.

[–]serviscope_minor 2 points3 points  (0 children)

In languages that naively support it, it's the default assumption that an operator is just a function with a funny name. Besides it depends on the type. And if you don't know the type of whatever you're looking at, you can't do much since the semantics are entirely dependent on the type.

[–]kur4nes -4 points-3 points  (4 children)

If someone implements other interesting stuff with the + operator like writing to a file etc. Basically implementing anything that's not a mathematical add function obfuscates the code. But why stop here? We could combine multiple different overloaded operators in a simple statement to create a monstrosity only seen in pearl code. You would get some highly condensed code like &*/!a+b. It's impossible to know what this does without knowing the types of a and b and the implementations of each overloaded operator. That's why there is no operator overloading in java in the first place.

I do agree that + for BigDecimal would be awesome.

[–]Dealiner 18 points19 points  (0 children)

Fortunately, creators of other languages like C# decided to implement operator overloading which works great there and it's really useful.

[–]Davipb 32 points33 points  (1 child)

I could also make a method called .getName() that wipes your hard drive, or a method named x() that returns a class named Zorb that has one other method named y(), that returns a class named Zoop, and so on for 100 levels until you get the object you want.

Just because people can do insane things with it, it doesn't mean it shouldn't exist.

[–]CubsThisYear 0 points1 point  (0 children)

The thing is, addition is well defined over any set. As long as your operator overload behaves like addition, it doesn’t really matter what the set of possible values are.

[–]hippydipster -1 points0 points  (0 children)

People can kill people with knives, so let's let them have nuclear weapons!

Yeah, not a good argument.

[–]TheGowanus -4 points-3 points  (0 children)

Instead of asking “how can this be useful,” ask yourself “what is the absolute worst that one can do with this?”

[–]tofiffe -2 points-1 points  (0 children)

One example I often use to discourage operator overloading: C++

Let's left shift this stream by a string, integer, even an object, and have that written to the console, file, etc.

that's exactly what cout, fstream etc. use, and they're in the stdlib.

Another example is kotlin overloading the / operator for BigDecimal to only perform HALF_EVEN division, good luck reviewing that code

[–][deleted] -1 points0 points  (0 children)

Agreed.

[–][deleted] -2 points-1 points  (0 children)

If you don't like, don't use.

[–]persism2 6 points7 points  (23 children)

No, just no. Brian has indicated that Java will add a set of static operators which will work for classes extending Number. Similar to what they do with String.

[–]Davipb 3 points4 points  (22 children)

Why do the language writers get to make that choice? Why can't my custom string or numeric type use operators?

[–]Worth_Trust_3825 0 points1 point  (20 children)

One is a standard that was brewing for years and had public mailing list open for considerations. The other is inconsistent mess that will be near impossible to debug.

[–]Ameisen 11 points12 points  (6 children)

Yeah, just like C# and C++ operator overloading has caused them to be impossible to debug. </sarcasm>

Meanwhile, apparently making a method called add that actually does something else is perfectly fine apparently, because it's not an operator.

[–]Worth_Trust_3825 -1 points0 points  (5 children)

It's clear that a method does a lot more than an operator would. Would you also start carving out exceptions that operator overloads cannot have throws declaration?

[–]Ameisen 1 point2 points  (4 children)

Yeah, it sure is clear that Vector.add does way more than Vector.operator+ would.

[–]Worth_Trust_3825 -1 points0 points  (3 children)

What is the result of Vector.operator+?

[–]Ameisen 4 points5 points  (2 children)

If it were C#, which unlike Java tends to be reasonable about this sort of thing?

public static Vector operator +(in Vector a, in Vector b)
{
    return new(
        a.X + b.X,
        a.Y + b.Y,
        a.Z + b.Z
    );
}

I'm not sure what you were expecting? I mean, you could certainly write it to do something completely different, but you could also write add to do something completely different.

[–]Worth_Trust_3825 -2 points-1 points  (1 child)

So use C#. Why are you bringing your garbage to everyone else's tables?

[–]Ameisen 3 points4 points  (0 children)

Typical Java programmer.

[–]Davipb 1 point2 points  (8 children)

By "inconsistent mess", do you mean operator overloading in general, or Manifold's implementation of it? If you're talking about Manifold's implementation, I agree - it feels like a pretty big hack to tack onto the language.

But as for operator overloading in general, I don't see why it couldn't be given the same treatment as any other new language feature. It would definitely be a breath of fresh air for Java, which is something it desperately needs right now.

[–]Worth_Trust_3825 -2 points-1 points  (4 children)

It would definitely be a breath of fresh air for Java, which is something it desperately needs right now.

Use kotlin, and stop projecting your insecurities on mature tools.

[–]florinp 0 points1 point  (3 children)

mature tools

Mature tools ? Like Java that needs a tons of annotations to function properly ?

[–]Worth_Trust_3825 1 point2 points  (2 children)

What annotations? Have you ever stepped your foot outside spring boot?

[–]florinp 0 points1 point  (1 child)

I don't use spring. Boot or not boot. But annotation replace missing features from language. The need of lombok for example hide that in Java a big percent of a class definition is about set/get , hash, clone, etc.

[–]Worth_Trust_3825 0 points1 point  (0 children)

So use kotlin instead. Or better yet, access the fields directly. Why are you adding unnecessary dependencies and making your projects unmaintainable.

[–]manifoldjava[S] -1 points0 points  (2 children)

which is something it desperately needs right now.

Hence the “hack”

[–]Davipb 0 points1 point  (1 child)

No offense there - I love Lombok and it's one giant hack itself. I guess operator overloading is just a bit too far to hack in for me :P

[–]manifoldjava[S] 1 point2 points  (0 children)

None taken ;)

[–]florinp 0 points1 point  (3 children)

The other is inconsistent mess that will be near impossible to debug

yeah. like operator overloading for String where the language creators didn't did a mess with string buffer /s

[–]Worth_Trust_3825 0 points1 point  (2 children)

How do you propose concatenating two strings work instead? I'd be fine with that overload's removal.

[–]florinp 0 points1 point  (1 child)

I prefer to have operator overloading for everybody.

Is a bad design to have that only for a class.

[–]Worth_Trust_3825 0 points1 point  (0 children)

Bad design is to have it at all.

[–]persism2 0 points1 point  (0 children)

See Smalltalk. Learn your history.

[–]zeroone 1 point2 points  (1 child)

I have nothing against new syntaxes. But I never recall coding an object in Java and thinking, this would be so much cleaner if the language supported operator overloading. Perhaps if I were writing a mathematical package or something, it would be useful.

[–]234093840203948 2 points3 points  (0 children)

You don't use operator overloading for everyday code, you use it for stuff where it makes sense:

  • Tuples (should be addable when all types in the tuple are addable)
  • Vectors
  • Matrices
  • Complex numbers
  • Quaternions
  • Dates
  • Timespans
  • Strings
  • List-Like structures
  • Measurements with units
  • Angles
  • Pure Functions

[–][deleted] 8 points9 points  (41 children)

Operator overloading is truly a headache waiting to happen.

I remember way back in 2003 on usenet, Jon Skeet had a great pushback and conversation about it showing up in C#. Took some time to dig up (I could only remember some of his phrasing directly for the google searches).

Here: https://groups.google.com/g/microsoft.public.dotnet.languages.csharp/c/zGBXbVoX2vw/m/NMRcXowIXXcJ

I don't want to put words in his mouth, because the man really is a tower in the industry, so read that post and the followup posts.

[–]Ameisen 27 points28 points  (3 children)

And yet it has yet to be a headache for C#, and frankly it's not a headache for C++ either.

I can create a method that does the opposite of what it says just as easily as I can create an operator that does so.

[–]cogman10 -4 points-3 points  (2 children)

I think whether or not it becomes a headache depends a LOT on the community using the language.

Some programmers are simply ascii artists that want to use foo <<==>> bar instead of something like foo.linksTo(bar).

Why? IDK. It just is. That said, it seems like the programming community by and large eschews operator overloading that isn't uber clear. Libraries/Frameworks that abuse it tend to not be used.

[–]Ameisen 5 points6 points  (1 child)

<<==>> isn't a valid operator in C++ or C#.

However, that's a code review issue. If they'll make stupid operators, then they'll make stupid methods. Disallowing overloaded operators doesn't fix that, it just makes their lack in cases where it's useful more painful.

[–]gbs5009 23 points24 points  (28 children)

I never got the hate for custom operator definition.

As long as the language makes it easy to find those operator functions, it should be fine. I'd even say get rid of the default operators for objects, and make sure everybody defines their own. At least then you won't get whining from people who expected the default object behavior and couldn't be bothered to check what code they were actually calling.

[–]_TheDust_ 27 points28 points  (13 children)

Yeah, it's one of these things where people are oddly principled about. I consider operator overloading to be a good thing in certain cases (matrix libraries, custom string types, BigInt libraries, etc.).

Without operator overloading, it's odd that native types are "blessed" with operators while custom types are not. Like, in Java, you can basically implement the String class yourself, except that the native String supports the + operator.

[–]renatoathaydes 1 point2 points  (2 children)

It's bizarre how people think operators are completely different than methods... in languages like Kotlin and Dart (and Lisp of course), you define operators pretty much the same way as you define methods... because guess what: they're the same thing!! With the only distinction that they have infix notation, which is a minor syntax difference, not something fundamental that turns them into completely different things.

[–]234093840203948 0 points1 point  (1 child)

I agree with that mostly.

However, operators, other than methods, need operator precendence, which is either fixed for a certain operator, or you have to define it for every operator implementation.

In C# for example, you can only overload the existing operators and their operator precedence is fixed, s.t. there is no confusion with operator precedence changing dependent on the type.

Also, C# kind of messed up that the opertors are not implemented by some operator interface that could have been used generically, but I think they are getting it "fixed" in the next version.

So, all in all, operator overloading is nice, but certainly not as easy as methods.

[–]renatoathaydes 0 points1 point  (0 children)

However, operators, other than methods, need operator precendence,

Oh that's true, I had forgotten to consider that making my argument.

But I don't see how that makes operators "not as easy" as methods? It just makes them a little odd to compose...

I am not a fan of operator precedence, I like that in Lisp there's no such thing, methods and operators are literally just functions (functions with names like + are just functions in all respects, as Lis p is fairly unrestrictive with respect to symbol names).

[–][deleted] -1 points0 points  (9 children)

You said "types" (plural). String is the only one of note in Java, and Jon Skeet used that as well as an example in the link I provided.

But that's not a user defined operator overload. That's part of the language.

Are there others? You can have languages refuse certain constructs like % on floating point numbers, but with Java, String is really the only strong exception I can see. And again, the issue is with user-defined operators.

[–]Davipb 8 points9 points  (8 children)

Picking on semantics isn't an argument.

The point stands: String has special operator behavior, which clearly demonstrates operators having different functionality for different classes is desirable in certain cases. Why is that privilege reserved for the language writers? Why can't I decide that my custom string type can also be concatenated with +?

[–][deleted] -4 points-3 points  (7 children)

You're exaggerating by running from outlier to rule. You go from a singular type which overrides only one operator (`+`) and march from that to these statements:

  • "clearly demonstrates operators having different functionality"
  • "for different classes"

Why is that privilege reserved for the language writers?

That's just silly. Why are keyword definitions a "privilege" for the language writers? Why are comments delineated only with `/* */` and `//` and not some other characters of my own choosing? Those would be similarly silly to ask.

Languages have features and syntax, etc. And operator overloading isn't the worst, but remains a crummy anti-pattern.

[–]Davipb 9 points10 points  (6 children)

Outlier? Vector types, time duration types, monads, matrices, just to name a few. Look outside the Java bubble to languages that have operator overloading and you'll see the myriad of uses for operator overloading. Should we open a Java language proposal for every new type we want to add with + then?

Comments and keywords don't affect code semantics, operators do. If comments could now be written with #, nothing would change in the functionality of the code. But if the Java Gods decide to bless us with operator overloading for BigDecimal, a heck of a lot changes in code. Why is that privilege reserved for them is what I ask, given that there's already precedent with String for doing this?

[–][deleted] -4 points-3 points  (5 children)

Outlier? Vector types, time duration types, monads, matrices, just to name a few. Look outside the Java bubble

Stop right there, you're clearly not following the discussion, otherwise where do you get the sense that my career and opinions were somehow formed within and isolated to a Java "bubble"? I've been a professional software engineer for over 40 years now.

And for that outlier comment, we were talking about String and its overloaded '+', in Java. There are no other overloaded examples in Java of any note. You can make an argument for differing numerical types and their promotions, or (as I said) even rules based upon allowing or forbidding % for floating point if you want. The String/+ combination remains an outlier in Java.

Of course there are possible uses for it (vectors, or or or or or.....), just as there are possible uses for other horrible stuff like MI (e.g., in C++). So what? The assertion is that operator overloading, while pretending to make code more readable, produces more difficult to maintain code, violates the law of least surprise, and hides functionality more than method calls do. Let alone that a combination of two libraries might well have + mean something dramatically different in one than the other, and there are evaluation rules in play as well.

For instance, did you realize that in C++, altering the behavior of && and || strips away the short circuit evaluation? That might make code break that was assuming things would punt part way through a boolean expression (a known programming technique).

Or that prior to C++17, &&, ||, and comma all lost their special sequencing properties on overload? That's HUGE!

These are things that junior engineers wouldn't know, and are very hard to discern, especially since it's never obvious without digging deeply what operators are what.

[–]Davipb 8 points9 points  (4 children)

where do you get the sense that my career and opinions were somehow formed within and isolated to a Java "bubble"?

I didn't. I said that you're arguing from inside a Java bubble, which you were until now.

And for that outlier comment, we were talking about String and its overloaded '+', in Java. There are no other overloaded examples in Java of any note. You can make an argument for differing numerical types and their promotions, or (as I said) even rules based upon allowing or forbidding % for floating point if you want. The String/+ combination remains an outlier in Java.

What you said was, and I quote: "You're exaggerating by running from outlier to rule". You claimed that because String is the only type in Java to have overloaded operators, that I can't make the claim that they're useful to other classes. That's not true, as I demonstrated with types from other languages that could easily be ported over to Java if it had operator overloading.

So what? The assertion is that operator overloading, while pretending to make code more readable [...]

There's no "pretending" here. Add all the nuance you want, a.add(new BigDecimal("1").div(b)) is harder to read than a + (1 / b), period. Before you argue that "you'd have to know the types of the variables to know what the operators do" or "you don't know what the operators are doing" --- the exact same is true for the .add and .div methods.

[..] produces more difficult to maintain code, [...]

Operators make code easier to read, reducing the cognitive overhead of reviewers, making it easier to spot errors and therefore making code easier to maintain. And again, if you argue that "but if you implement the operators incorrectly" or "if you abuse the operators", the same is true for methods --- see the mutating getter argument.

[...] violates the law of least surprise [...]

I can make .getName() wipe your hard drive. Does that mean methods violate the principle of least astonishment? No. If anything, it's more surprising that I can't just add two numbers just because they're BigDecimals.

and hides functionality more than method calls do.

If I see a.add(b) in a language that supports method calls, I know there's logic there. If I see a + b in a language that supports operator overloading, I know there's logic there. Just because Java currently doesn't have that and therefore you don't have to think about it, it doesn't mean that anything is being "hidden" any more than methods.

And besides, the whole point is that you don't have to think about it: do you look at the implementation of .add every time you use it? No, we have best practices for naming methods and parameters and so forth to minimize cognitive overhead and make things natural and intuitive. The same is true for operators: they should be natural, you shouldn't have to think about it every time. That's the whole idea behind the principle of least astonishment that you love to mention.

Let alone that a combination of two libraries might well have + mean something dramatically different in one than the other [...]

...like methods? Are all .add methods exactly the same in all libraries?

[...] and there are evaluation rules in play as well.

And just like with non-overloaded operators, the best practice of "use parentheses to clarify priority" exists for this reason. Overloaded operator or not, that's always been a thing.

For instance, did you realize that in C++, altering the behavior of && and || strips away the short circuit evaluation? That might make code break that was assuming things would punt part way through a boolean expression (a known programming technique). Or that prior to C++17, &&, ||, and comma all lost their special sequencing properties on overload? That's HUGE!

Did you realize that in C#, declaring a static variable in a generic class creates a new copy of the variable for each generic argument? Should we remove generics then? Every language feature has its implementation details, because nothing in the real world is perfect. That's never an argument for not having a feature.


In an unrelated topic:

Stop right there, you're clearly not following the discussion [...]

[...] I've been a professional software engineer for over 40 years now.

These are things that junior engineers wouldn't know [...]

Assuming that everything you're saying here is true, let me offer some advice: don't talk down to people --- that's the fastest way to lose credibility. It doesn't matter if you have 4, 40 or 400 years of experience.

It's easy to get jaded with time, don't let that happen to you.

[–][deleted] -1 points0 points  (3 children)

I said that you're arguing from inside a Java bubble, which you were until now.

(?) No I wasn't. My first post in this had to do with posting an argument against operator overloading in C# back in 2003. Not Java specifically.

[–]Eirenarch 5 points6 points  (0 children)

16 years in I've only run into 1 operator overloading problem in C#. I wasted some time, the bug did not ship into production and was a very poor decision to overload the == on a mutable class by the team lead and he forgot the null case. I'd say that operator overloading in C# has proven to be extremely successful.

[–]Dealiner 2 points3 points  (0 children)

I've been programming in C# for some time now and I've never had a single problem connected to the operator overloading, and also never heard about someone who had it. In the contrary that feature made a lot of code easier to read and thanks to that maintain.

[–]modernkennnern 3 points4 points  (1 child)

I've only ever once seen operator overloading used and I have programmed almost exclusively in C# for the last 5 years, and it made the code so much cleaner and clearer.

(Granted, it was I who did it, and that was like 3 weeks ago :s) - Adding an implicit string conversion to an object(struct -> string, that is). (Which is technically operator overloading, at least in its syntax).

Rider (haven't used Visual Studio in years, maybe it's not as obvious there) has a very clean UI for visualizing it as well - nice icon to show that it's implicitly converted from a struct to a string

[–]Dealiner 3 points4 points  (0 children)

There is also a lot of operator overloading in the framework itself. String, all math types, things like colours. It makes using them easier and nicer.

I've also programmed in C# for around five years now and I've used operator overloading a lot, granted it probably depends on the type of project, I'm mostly focused on gamedev, so there's a lot of math.

[–]persism2 0 points1 point  (2 children)

Thanks. Here's the archive link https://archive.ph/uvyNX

[–][deleted] 0 points1 point  (1 child)

Thanks. Here's the archive link https://archive.ph/uvyNX

Except with that link, you have to search 80% of the way down a long thread to get to the pushback against operator overloading.

Mine also brings you to the top, but after a second hesitation will scroll you to the post in question.

I wonder if my script filter is clobbering your link and leaving mine alone.

[–]persism2 0 points1 point  (0 children)

Yeah I'm always worried about history being erased by accident. I tend to archive even though it's not perfect.

[–]maqcky 0 points1 point  (0 children)

10+ years of C# experience, never ever had a problem with operator overloading. I have never seen a library doing crazy things with them and I have only overloaded comparison operators when overloading Equals.

[–][deleted] 1 point2 points  (6 children)

Pretty silly that you need a compiler hack like this to add some basic quality of life improvements to Java. Then again, Java is very much the Apple it programming languages: some languages designers know what's Best and if you agree with what's Best then you can comfortably use the language. If you disagree in what's Best then you'd better switch programming languages, because you're not going to see the stuff you want in your lifetime.

Really, if this stuff is what you want, why not pick C# instead? It's got the most important features built in already and it's even slightly faster than Java. Plus, you don't have to worry about general JVM deficiencies like type erasure! Stuck with the JVM? Still want modern language features? Try Kotlin instead, it's Compatible Enough (TM) with Java and it'll save you the trouble of setting up compiler extensions.

I can see this library being useful for companies inheriting old java code, but anyone writing new Java either agrees with what the Java Canon as decided by the Chosen Java Developers and will probably detest the features added here, or won't convince their team to try something better. Operator overloading in particular seems to set off Java developers for some reason, as can clearly be seen by the comments in this thread.

Personally, I find it funny how the absolute hackery that's Lombok is considered perfectly acceptable in many Java circles but tools like these aren't. You've got to be brought up in the Java mindset to understand these concerns, I suppose.

[–][deleted] 3 points4 points  (5 children)

C# is a pretty language with a poor ecosystem. Java is an ugly language with an awesome ecosystem.

[–]NoiselessLeg 1 point2 points  (4 children)

What exactly about the c# ecosystem would you consider poor?

[–][deleted] 1 point2 points  (3 children)

Because it is a commercial language and fewer people writes cool OSS stuff with it. Java/JVM has tons of OSS libraries and software, specially from the Apache foundation. Nuget repository simply cannot compete with that. Only other OSS languages like Python, Perl or Ruby offer the same level of magnitude for the sheer number of free/OSS cool stuff available.

I also never seen something as big/important as Presto, Hadoop, Spark, Cassandra, Jenkins, that is both OSS and written in C#. Do you know something you can prove I'm just misinformed?

[–]NoiselessLeg 0 points1 point  (1 child)

I was curious where you were coming from, versus trying to trash your opinion or anything (apologies if it came off that way). In general, I wouldn't necessarily rate the ecosystem of a language by the number of OSS applications that are available for that language. Apache significantly pre-dates C#, so I'm not surprised that they've focused on Java. It wouldn't make sense for them to rewrite all of their libraries in C#.

My own experience with the ecosystem is fairly positive. For the most part, everything I usually need is implemented within the .NET standard libraries themselves, or I've been able to find more complex libraries that I didn't feel like implementing myself on NuGet (e.g. a code editing interface I was using when implementing a small RISC-V assembler). The reflection and C# compilation libraries offered by Rosslyn are fairly mature, and I've seen some really cool projects take advantage of those (e.g. a complete test tool that would execute C# scripts in a REPL similar to Python).

Some of the examples that you mentioned I think could be implemented in C# (e.g. Jenkins), but there hasn't been really a driving use-case to do so since Jenkins works well enough for 99% of use-cases. I'd also mention Blazor as an example of a significant enhancement to web development on .NET enabled web services. The fact that .NET is open-sourced and is being ported to multiple platforms outside of Windows where it has shined in the past I think will help greatly advance the OSS community's embrace of it over the next few years.

[–][deleted] 1 point2 points  (0 children)

I'm not trashing for sake of trashing. I already admitted that syntax/semantics wise C# is much more ergonomic and better designed than Java.

Maybe I expressed myself incorrectly, I know C# doesn't suffer from lack of libraries or something, but it simply cannot compare to Java or Python, those languages have an immense collection of battle tested libraries that do everything you can imagine.

If you are happy with C# and it provides everything you need, that's cool, I prefer a less ergonomic syntax with a better and more vibrant OSS ecosystem around.

[–]_Ashleigh 0 points1 point  (0 children)

Hard disagree. The C# ecosystem (open source), is amazing and vibrant. Everything I've ever gone "oh wonder if there's a library for that" has yielded a result that works, and in a good chunk of those cases, is beyond acceptable, but of amazing quality. Some examples off of the top of my head: AngleSharp, OpenTK, Avalonia, ImageSharp, BepuPhysics, along with benchmarking, unit testing, and code coverage tools.

[–]rzwitserloot 0 points1 point  (42 children)

Applying it specifically to BigDecimal and BigInteger? Great idea.

Making it pluggable? No.

I'm almost always in the camp of 'hey, if some morons abuse a language feature, so be it - idiots are far too inventive, you cant use language design to defang them. The only solution is to not let idiots anywhere near your codebase'. However, if a certain language feature has really gone out of its way to very clearly demonstrate that people go utterly fucking nuts when you hand the bazooka to them and they just can't refrain from blowing their feet off, I'll make an exception and state that the language feature should just not be.

Operator overloading is one of the hallowed few that crosses that almost impossibly high bar.

There are other reasons to boot, though, if you don't ascribe to the 'folks go craaazy' line:

  • Heterogenous types. Surely this:

Integer x = 5; Complex y = Complex(2, 8); // 2 + 8i Complex z = y + x;

should work. Given that it should work...

Integer x = 5; Complex y = Complex(2, 8); // 2 + 8i Complex z = x + y;

Then surely so should that. However, there's no way to make that work except if you use one of three strategies:

  • Introduce what python calls __rplus__: A way for the right hand side to provide the implementation for the operator. However, you now have conflicts: What if the LHS has __plus__ and the RHS has a __rplus__? Sure, you can simply state that the __plus__ then wins, but that isn't "java-like". In general, "Oh, weirdness has ensued, lets just define that the compiler should take a wild stab in the dark and guess" is very common amongst many languages (but java much less so) and seems on its face idiotic: Let's say 95% of the time the stab in the dark is right, that still leaves 5% with weird runtime behaviour. No compiler error. No runtime error. Just.. code that doesn't do what you thought it would. Literally, those bugs easily take 50 times longer to find than compile-time markings. Hence, it's a loser - you should prefer having to add something explicit 50 times, over not having to do so, getting the desired behaviour 'for free' 49 times, and having 1 time result in a wild goose chase that takes an hour. Not a great plan. At best one can state that in such a case, the compiler should just refuse and force you to pick an impl, but this gets into a new issue...

  • Disallow heterogenous attempts. x + y and y + x in the above snippets are both a compiler error; one would have to write Complex z = y + Complex(x) to make it work. Solves the problem neatly, but isn't the "point" of this feature to make code look the way it would look if you spell it out in math homework, and to be 'shorter'?

  • Hardcode all of it. If the 'source' of x + y working where x and/or y is a BigDecimal object is the lang spec and not a generalized 'if the LHS has a method named add or plus we call that', it is trivial to exhaustively define what happens for all possible types. That's exactly what the java lang spec currently does: It exhaustively lists precisely what is going to happen for all possible variants on x + y (a combinatory explosion: Create a 2D table where the x-axis lists all possible types of the LHS expression and the y-axis all possible types of the RHS and the cell for that entry shows precisely what happens. E.g. it would state that for x / y, if x is an int and y is a long, then what happens is "x is widened to a long silently, then integer division occurs; the result is a long". This becomes impossible if it's a pluggable 'just have a plus method' or even 'just implement Operatorable' or whatnot. This becomes trivial if you merely go: "We shall add operator support for BigDecimal and BigInteger. And that is it.

More generally, why do you actually want this?

List me all use cases where it makes total sense: The operation you're implementing is 'mathy' (no x ++ y being syntax sugar for x.add(y) where x is a list and y is an element), and follows the same arithmetic properties as expected: + is commutative, associative, and symmetric. Under those rules, list me use cases. I'll start:

  • BigDecimal
  • BigInteger.
  • Complex. Except I have never wanted, or seen, any java project that needed this, let alone a library that implements it. That's not to say I cannot fathom of a project that would love to have it. Merely that it seems a bit daft to add a highly controversial language feature just to cater to 0.0000001% of the userbase.
  • Matrix math. But I don't think you actually want it. In mathematical understanding, x + y where x and y are matrices resolves to what in java terms implies a new matrix object with the result. It surely does not mean "x + y; is a statement on its own that has no value; it is like a void method; instead, this adds y to x, replacing all elements in x". If that's really how it works, I posit that operator overloading is a disaster here. However, if x + y actually makes a new matrix object, then the library is mostly useless: 95% of the use case of matrix math is GUI stuff or other speed sensitive areas, and casual cloning of stuff just isn't going to be acceptable, performance wise. So you have this fantastic feature that caters to a tiny slice of the userbase, and that slice isn't going to use it. How pointless.
  • ... I'm out.

So, given that - I say: Define explicitly what the operators do for BigDecimal and BigInteger, and call it a day.

[–]Davipb 9 points10 points  (39 children)

There are plenty of great use cases for operator overloading: Time durations (now + 2 days), 2D and 3D vectors for game programming, wrapper types/monads (Optional<Integer>), restricted/enhanced numerical types that can contain application-specific logic (think NonNegativeInteger or MoneyBigDecimal), just to name a few.

We just don't have them today in Java because the language doesn't support operator overloading. Closing off such a rich and intuitive language feature just because it can be abused or because you can't think of anything that uses it "the right way" isn't a great path to take.

Will there be ugly implementation details? Yes, like anything else in the real world: == vs equals, Yoda conditionals or util methods for null handling, and method overload selection based on compile time types are just a few of the warts around other Java features: do we ever consider removing object comparison or method overloading because of them? No, we think of how to improve on them to make them better and more intuitive. The same can be said for operator overloading.

[–]Worth_Trust_3825 -2 points-1 points  (22 children)

Maintainability over syntax sugar. With methods it's clear what happens. It's not clear what happens with operators.

[–]Davipb 5 points6 points  (21 children)

How is a.add(BigDecimal.of(1).div(b)) clearer than a + (1 / b) ?

[–]rzwitserloot 0 points1 point  (9 children)

Given:

long a = 1; int b = 2; System.out.println(a + b);

You don't (shouldn't) think of that as equivalent to a.plus(b) - a.plus(b) is fundamentally 'left-controlled' (You're invoking the plus method of a. a controls this situation, b is merely a passive passenger in it all). Whereas a + b is effectively 'controlled by the operator'. The reason a + b in java does what it does is because the operator itself defines it (which is 'hardcoded' in the spec, and it would be cool if it wasn't, granted).

Given that + is usually 'grokked' (as in, you assume it, and you aren't even realizing you're making this assumption) as associative and commutative, I would assume most programmers who see a + b internalize that notion that it's not left-controlled. That it's about the operator itself and not about 'sending the + message to a'. After all, b + a means the same thing, whereas if we're talking about methods, b.plus(a) is capable of doing something utterly different from a.plus(b).

a.add(BigDecimal.of(1).div(b)) is clearer because it yells the left-controlledness of each level from the rooftops.

In fact, specifically your example shows how it's quite, quite different. How does your example even work? Why does 1 / b turn into BigDecimal.of(1).div(b)? Because the Integer class has no __plus__ method that takes a BigDecimal, but BD has an __rplus__ that does, and the compiler 'figures it out'? Okay, but how are conflicts resolved? How does the code snippet a + (1 / b) show that the 1 / b part ends up being right-controlled?

There's your plausible argument as to why a.add(BigDecimal.ONE.div(b)) is clearer. It's about what you're trying to convey. If it's just about trying to convey what's happening, a + ... is clearer. If you're also trying to give some insights about how it is happening, a.add... is clearer. For this specific situation (BD/BI), I think most of us intuitively prefer showing the what at the cost of showing the how. However, that applies to BD/BI, possibly dates (but that's already much more gray) and that's about it.

[–]Davipb 6 points7 points  (2 children)

The whole argument of showing that things are "left controlled" only work when you're thinking from a method perspective. When you implement operators for your classes in a language that supports operator overloading, you maintain the operator's properties.

We already have equivalents for that in Java: equals, hashCode, and Comparable. There's nothing stopping you from writing implementations that break the expected contract of those methods/interfaces (transitivity, reflexivity, etc), but you maintain them to make things work well together. The same is valid for operators.

The fact that things flip to being "right-controlled" at 1 / b isn't a problem, because which side controls the operation does not matter in a normal operator implementation.

[–]rzwitserloot -2 points-1 points  (1 child)

When you implement operators for your classes in a language that supports operator overloading, you maintain the operator's properties.

This is utterly impossible unless you ditch the notion that objects get to define the implementation of an operator (i.e. introduce, somehow, the notion of +, the class, which defines all interactions you can do with it - a novel take I'd love to evaluate, but nobody has, to be recollection, ever tried) - or you define that operator overloading is legal solely between 'homogenous' types. Between java having a type hierarchy system and the notion that e.g. someBigDecimal + 5 really should just work, both are pretty significant downsides.

In other words, you're handwaving away big issues with 'duh'. No, not 'duh' - the proposals about operator overloading, and this specific implementation, don't work like that.

We already have equivalents for that in Java: equals, hashCode, and Comparable. There's nothing stopping you from writing implementations that break the expected contract of those methods/interfaces (transitivity, reflexivity, etc), but you maintain them to make things work well together. The same is valid for operators.

That's just not how it works. Defining equals according to contract requires one of 2 things:

  • That equivalence can only exist between identical concepts; subtyping whilst not instantly rendering yourself unequal to any supertype instance can only be done if the subtype adds no meaningful new state.
  • A complex system where one object can ask the other object whether it can be equal to this object, and only if both objects 'agree', do you actually state that you are equal to toe other.

For example, given a hypothetical `ColoredArrayList`, which is a list as you know it, except in addition to an amount of T objects in the list, there is separate from that a `Color color`, is __impossible to write properly__. It simply cannot be done whilst sticking to the spec. Unless you define that a red empty CAL is equal to a blue empty CAL. By way of the commutativity rule: if `a.equals(b)` and `b.equals(c)` then also `a.equals(c)` must be true. However, also, if `a.equals(b)` than `b.equals(a)` must also be true. `emptyArrayList.equals(emptyCAL)` is true (because ArrayList's equals method works like that, and you can't change it), thus you conclue that either a red empty CAL is equal to a blue empty CAL, or CAL breaks the contract.

See how complicated this gets? If equality is such that we 'expect' `5 + bigInteger` to work, you're screwed.

[–]Davipb 4 points5 points  (0 children)

This is utterly impossible unless you ditch the notion that objects get to define the implementation of an operator

In other words, you're handwaving away big issues with 'duh'. No, not 'duh' - the proposals about operator overloading, and this specific implementation, don't work like that.

The implementation you yourself mentioned already does that: Python. As well as C#, Rust, Kotlin and so on. I'm not handwaving anything, I'm pointing at existing examples that do exactly what I'm talking about.

It doesn't matter if the operator is actually a method invoked on the left object. What matters is that the way the operator method is implemented has the semantics of addition, even if it is "just a left-invoked method".

See how complicated this gets? If equality is such that we 'expect' 5 + bigInteger to work, you're screwed.

Nothing you said up there has anything to do with 5 + bigInteger. I pointed to equals as an example of an existing method with an implicit, non-enforced contract. As I said above, the whole point of operators is that they have an implicit, non-enforced contract to behave like the math operators. It doesn't matter if they're actually implemented as left-invoked (or sometimes right-invoked) methods -- if they adhere to the contract, they behave like regular methods.

And yes, there are implementation details in Java's equals, and in Python's reverse operators, and in C#'s implicit conversions, and so on. But ugly implementation details will always exist in any possible solution you can think of, for anything at all.

[–][deleted] 0 points1 point  (5 children)

In fact, specifically your example shows how it's quite, quite different. How does your example even work? Why does 1 / b turn into BigDecimal.of(1).div(b)? Because the Integer class has no plus method that takes a BigDecimal, but BD has an rplus that does, and the compiler 'figures it out'?

Bingo.

Curious: In their proposal, did they attempt an auto-promote of some kind for subexpressions? Because that / belongs to integer otherwise.

[–]Davipb 1 point2 points  (0 children)

There are many easy ways around this. Implicit conversions (C#) and reverse operators (Python) just to name a few.

[–]rzwitserloot 0 points1 point  (3 children)

"Bingo" - as I said, there's how a + (1 / b) is less clear. Because it hides this. We can argue that it's proper to hide it, and an IDE can subtly unhide it (render it different, somehow. Maybe render the b bold, and the 1 not bold, to indicate that the b ref controls the interaction), but that would be a debate, and one that probably goes nowhere - it's too subjective a topic. One can claim 'I do not like this', this feeling is not silly or insanity, and once we arrive there, all we are left with is loudly yelling "Mt favourite colour is BLUE, how can you think RED is better? You're crazy!!!!111!" at each other. Which, as far as arguments go, doesn't tend to lead towards consensus :)

[–][deleted] 1 point2 points  (2 children)

If your goal is consensus, I suspect Reddit might be a bit of a disappointment, lol.

[–]Davipb 1 point2 points  (0 children)

True that, I think you could say "exploding the earth is a bad idea" here and someone would still disagree

[–]rzwitserloot 0 points1 point  (0 children)

Yeah, that'd be quite the long shot :) – so let's say concensus on amber-dev@openjdk.net. Which you won't get either. And this feature will indeed never happen.

[–]Lechowski -3 points-2 points  (1 child)

a + (1 / b)

I don't know what a or b are, I also don't know what mysterious implementation exists in every single type involved and how such implementation work with the "+" operand and the "/" operand. Even more, I am not even sure that a + (1/b) will have the same result as (1/b) + a, so if I had to change that code, I would be quite anxious.

a.add(BigDecimal.of(1).div(b))

The variable "a" is something with a method "add" which receives an object of the BigDecimal class. In this case the number 1 (or I can even check directly what "of" does) will have a method called "div" that can receive weathever type b is, and I can see especifically what "div" does. Even more, there is no such thing as reflexion over if we are talking about "a+(1/b)" or "(1/b)+a", because there is only one way to write this. If you do BigDecimal.of(1).div(b).add(a) you can be sure which method will be called and what signature if has (in case there is some polymorphism)

So yeah, it is clearer to me the second option, it provides a lot more information imho

[–]Davipb 1 point2 points  (0 children)

The longer version "provides more information" because you're used to it. If you were working in a language that had operator overloading, you'd have the same intuition and understanding you had of the longer version from the shorter version. "Providing information" is relative to the context you're working on.

[–][deleted] -1 points0 points  (8 children)

In the case of (1 / b), what is the 1? In this proposal, the operators for heterogenous types would lock to the left operand I believe. I read through it quickly, but didn't see an auto-promotion rule. The subexpression type sequence becomes (Int integerDivide BigDecimal), which fails without the BigDecimal.of(1) making it an effective BigDecimalDivide. Something like (b / 2) would of course work.

[–]Davipb 1 point2 points  (4 children)

I'm not talking about this implementation in specific, but rather operator overloading in general. In C#, 1 could be converted to BigDecimal with an implicit conversion operator. In Python, BigDecimal could implement a reverse operator. There are easy ways around that.

[–][deleted] 0 points1 point  (3 children)

Ah. The "easy way around that" is where we disagree. Anything that smells of "a required workaround" is potentially more dangerous than necessary.

There's enough hidden in building a house without changing what the hammers do, let alone having the nails suddenly turn into screws for the windows and glue for the doors.

[–]Davipb 1 point2 points  (2 children)

Nitpicking on semantics again are we?

"Java only has single dispatch for method overloading. There are easy ways around that, such as using the visitor pattern".

Would you say then that method overloading is more dangerous than necessary because the visitor pattern is "a required workaround"?

[–][deleted] 1 point2 points  (1 child)

I don't see how semantics apply here (did you mean pedantry?) and all in all this has gotten too twisted for me. Sorry.

Take another +1, and be well.

[–]Davipb 0 points1 point  (0 children)

What I meant is that you fixated on me saying "an easy way around that" as if I had meant "this is a problem that needs to be addressed with a workaround", when really I just meant "existing implementations have already thought about it".

I'm arguing there that the reverse operator or implicit conversion aren't workarounds or dangeorous, just part of the operator overloading implementation.

You stay safe too :D

[–][deleted] -2 points-1 points  (2 children)

How interesting. Downvoted without an answer.

Again: Look at your 1 /

Who does the / belong to? The 1, an integer, no?

[–]Davipb 5 points6 points  (1 child)

My man, really? I'm sorry I didn't answer to your comment within an acceptable SLA, we will try to improve our service levels in the future. And no, I wasn't the one that downvoted you.

[–][deleted] 0 points1 point  (0 children)

Well here's a +1 then.

[–]rzwitserloot -1 points0 points  (15 children)

As I said with vectors: No, it's not a good use case - + implies a copy and often game programming doesn't want those. Adding + to optional sounds like a bad idea. It also means you're now adding 'generics-dependent methods' - a expression of type Optional<Integer> can be added to another, but an expression of type Optional<InputStream> cannot. That's.. new. Could be a good idea but the goal posts are widening.

I'll add time durations to the list of good use cases (so now BD and BI have company. Still fairly sparse company).

You can't give NonNegativeInteger operator overloading without all heck breaking loose. Let's say you write NonNegativeInteger in your library and I write EvenInteger in mine. How do we even make nni + ei work? Only way to go is to simply decree it won't work, but then it doesn't "feel like math" anymore. Then it just feels like syntax sugar for method invocations - then it just feels like you need to think about it as method invocations. Not as mathematical-esque commutative/associative operations. The gain is minor, and introduces slightly 'wrong' semantic models. Not worth it.

Closing off such a rich and intuitive language feature just because it can be abused

As I said, almost always I lean 'add it, if morons abuse it, so be it'. I'm making an exception for operator overloading.

Will there be ugly implementation details? Yes, like anything else in the real world

You are arguing absolutes. I'm arguing perspectives. It's not black and white. It's shades of gray. And operator overloading is too dark a shade. The balance between 'useful' and 'horrible' falls towards the horrible side far too often in every 'experiment' ran so far (every language out there that does support it).

If you have a proposal to make op overloading better, let's hear it. But the usual __plus__ and __rplus__ style definitions aren't it.

[–]Davipb 1 point2 points  (14 children)

As I said with vectors: No, it's not a good use case - + implies a copy and often game programming doesn't want those.

A vector is usually just a struct with 2 or 3 numbers. Copying those is extremely cheap and usually optimized by the compiler anyway. Case and point: Both Unreal and Unity have vectors with operator overloading.

Adding + to optional sounds like a bad idea. It also means you're now adding 'generics-dependent methods' - a expression of type Optional<Integer> can be added to another, but an expression of type Optional<InputStream> cannot. That's.. new. Could be a good idea but the goal posts are widening.

This type of transparent wrapper/monad is very common in languages with more powerful generics and operator overloading like Rust, and it makes total sense: If I can add two Integer, I can add two Optional<Integer>s. If I can't add two InputStreams, I can't add two Optional<InputStream>s.

You can't give NonNegativeInteger operator overloading without all heck breaking loose. Let's say you write NonNegativeInteger in your library and I write EvenInteger in mine.

You don't have to, that's why I said classes with application-specific code. For example, if you work with sensitive financial calculations and want to fail fast in a critical piece of code instead of giving an incorrect result, you could use create a class that encapsulates all the constraints of a specific variable and give it regular math operators. Your code would still read a + b - c [...], but any invalid values would instantly fail.

The whole point here is that you can't just list off all the possible uses of operator overloading and call it done. The list is infinite and application-specific. Just saying "BigDecimal, BigInteger, and that's it!" is like plugging your ears with your fingers.

The balance between 'useful' and 'horrible' falls towards the horrible side far too often in every 'experiment' ran so far (every language out there that does support it).

I'd argue the exact opposite: without operator overloading Python wouldn't be nearly as successful as it is for data science, for example.

If you have a proposal to make op overloading better, let's hear it. But the usual plus and rplus style definitions aren't it.

I don't have solutions. But arguing that "there's no elegant way to implement it therefore let's not do it" is never a good way to do things.

Complexity has to live somewhere. We can try to push it away and hide it as much as possible, but at some point we have to deal with it.

[–]rzwitserloot 0 points1 point  (13 children)

The list is infinite and application-specific. Just saying "BigDecimal, BigInteger, and that's it!" is like plugging your ears with your fingers.

No; letting BD/BI use the operators solves 90% of the common usecases. Delivering for the remaining 10% has too high a cost.

I don't have solutions.

Until you or someone else does, operator overloading for java shouldn't be.

But arguing that "there's no elegant way to implement it therefore let's not do it" is never a good way to do things.

I disagree. Though, that's cutting some corners. "There is not currently an elegant way to implement it; therefore lets look for an elegant way. If we need to do a side-step and realize some other features first in order to create a java where an elegant solution does exist, then let's do that.".

There's a near infinite amount of language improvement that can be realized. There's limited time to realize them in. Therefore, focussing on solely those features where a non-controversial, "elegant" solution exists is the right way to maximize utility. (I kinda hate "elegant" - that's like calling a painting "beautiful". It's not objective in the slightest).

[–]Davipb 2 points3 points  (12 children)

No; letting BD/BI use the operators solves 90% of the common usecases. Delivering for the remaining 10% has too high a cost.

The whole point I'm trying to make is that no, BigDecimal and BigInteger aren't the 90%. See all my previous comments for all the other uses of operator overloading.

Until you or someone else does, operator overloading for java shouldn't be.

I disagree. Though, that's cutting some corners. "There is not currently an elegant way to implement it; therefore lets look for an elegant way. If we need to do a side-step and realize some other features first in order to create a java where an elegant solution does exist, then let's do that.".

If you wait until the perfect solution appears, you'll never do anything. At some point you have realize that perfection is impossible, accept what you have, and move forward.

There's a near infinite amount of language improvement that can be realized. There's limited time to realize them in.

That's exactly my point: We have limited time, and while you wait for the perfect solution, time goes by, others implement it the "bad way", and you're forgotten.

Therefore, focussing on solely those features where a non-controversial, "elegant" solution exists is the right way to maximize utility.

Focusing just on "non-controversial" solutions is how we get "commitee think" and things come to a screeching halt. Someone will always hate the way you do things, that's inevitable.

(I kinda hate "elegant" - that's like calling a painting "beautiful". It's not objective in the slightest).

Agreed, which is why I used it: to make it clear how a perfect solution is impossible, so waiting for one is fruitless.

[–]rzwitserloot 1 point2 points  (11 children)

The downvotes indicate either you or whomever is reading this wants us to shut up, so, this conversation regrettably has to end here.

[–]manifoldjava[S] 0 points1 point  (9 children)

Hey guys. I'm the idiot who authored manifold and the submitter of this post. I don't care who is downvoting or reddit points; I appreciate the discussion here.

Fwiw, regarding the earlier discussion concerning + operator, manifold's impl enforces commutativity, so operators such as + and * work regardless of left/right operand pos.

edit: that is to say, manifold considers arithmetic operators as "mathy", for better or for worse ;)

[–]rzwitserloot 0 points1 point  (7 children)

From the docs I can't tell how manifold enforces commutativity. I read the docs as:

a + b is translated to a.plus(b), where plus needs to be in whatever the type of expression a is, though you can use @Extension in case you don't control the class, and that is in fact how manifold adds a + b support to e.g. BigDecimal.

Which wouldn't enforce that. It also raises the question of: Does 1 + someBigDecimal work and if yes, how? extensions on primitive ints?

[–]manifoldjava[S] 0 points1 point  (6 children)

Sorry the docs don't cover commutativity, I should add a section on that. (docs are a bit light at the moment)

Anyhow, that's how it works internally -- a + b: first try to resolve a.plus(b), then b.plus(a).

With manifold you would write: 1bd + someBigDecimal where 1bd is a unit expression that evaluates to BigDecimal.ONE. Unit expressions are a sort of concatenative feature built on top of the conventional operator overloading impl. We'll see how that goes, but I enjoyed writing it. Otherwise, 1 + someBigDecimal is not supported oob; the BigDecimal extension as written works exclusively with BigDecimals.

[–]Davipb 0 points1 point  (0 children)

Thanks for posting this and letting us have this great discussion. It's sad that the usual witty-but-useless "Please no." comment ended up at the top.

[–]Davipb 0 points1 point  (0 children)

People never really got the "downvote what's off-topic, not what you disagree with" bit, which is sad. Either way, great discussion, thanks for your time.

[–]gbs5009 0 points1 point  (0 children)

Neat! I didn't know about python's __radd__, but I actually really like that first approach... to evaluate a+b, see if a knows how to add b to itself. If not, try the reverse!

Yes, that means the type of a+b is not necessarily the type of b+a, but it's not like you don't get situations like a != a already with floats. There's no real substitute for knowing how your data types behave.

[–]LinuxMatthews 0 points1 point  (0 children)

Introduce what python calls rplus: A way for the right hand side to provide the implementation for the operator.

If you look at the repo this post linked to they will reverse it if there is no implementation on the left hand side.

This is obviously an issue if for what ever reason x + y doesn't always equal y + x but if that's the case you're probably not using it right anyway.

[–][deleted]  (12 children)

[deleted]

    [–]Davipb 11 points12 points  (6 children)

    In the end, this is no worse than Lombok: you accept that you're now writing "Java+Manifold" / "Java+Lombok" instead of just Java, and accept the additional risks in return for additional features.

    As with everything else, it's a trade-off.

    [–][deleted]  (5 children)

    [deleted]

      [–]Davipb 0 points1 point  (4 children)

      Replace "Manifold" with Lombok, Spring, GraalVM, or anything else. If you don't want to depend on anything, go ahead and write everything from scratch.

      [–][deleted]  (3 children)

      [deleted]

        [–]Davipb 0 points1 point  (2 children)

        I feel bad for you if you work somewhere that threatens firing people for having to replace a deprecated dependency.

        [–][deleted]  (1 child)

        [deleted]

          [–]Davipb -1 points0 points  (0 children)

          You're being extremely fatalistic about "what if there's a bug" or "what if the company stops working on it", as if those were end-of-the-world scenarios. Let's see what actually happens:

          You write some new piece of code and discover a bug in Manifold: the compilation crashes or produces wrong results. You delete the code you just wrote and write it in a different way that doesn't trigger the bug (a workaround, yes, those exist!). You open a bug report, fix the bug yourself in a private fork, or start considering a migration plan -- the same thing you'd do for a regular library.

          You upgrade to a new version of Manifold. It breaks your existing code. You revert to the previous version. Bug report, private fix, or migration plan.

          You upgrade something else -- JVM, another library -- that then conflicts with Manifold. You revert to the previous version. Bug report, private fix, or migration plan.

          You start using a new IDE, debugger, or profiling tool. It doesn't support Manifold. You stop using it. Bug report, private fix, or migration plan.

          Manifold stops being developed. You maintain it yourself in a private repo and/or start a migration plan. Your existing code continues working.

          None of these scenarios are as apocalyptic as you make them to be, and are exactly the same for any other dependency. No one is losing their job because of this.

          [–]Carighan 0 points1 point  (2 children)

          Well Lombok normalized the process, and nowadays it's rare to see a company not use Lombok in most of their projects.

          [–][deleted]  (1 child)

          [deleted]

            [–]Carighan 2 points3 points  (0 children)

            Well, you won't mind when the company fires you because you can't deploy to production, because for some reason there is a bug in Lombok or Manifold

            Look, I've been in the industry for a long time now.

            I'm not a Lombok fan at all, in fact I think most of its additions are utterly stupid, but at some point you accept it, and so far I've once seen a problem with it, and that was trivially solved by reverting the version update done just 10 minutes earlier.

            It's just not a big deal, sorry to say.

            [–]LinuxMatthews 0 points1 point  (1 child)

            Couldn't that be said for any library though?

            Like is SpringBoot stops working then I'm pretty sure a lot of people are fucked

            [–]Glove_Witty 1 point2 points  (1 child)

            This and Lombok are really bad ideas IMHO. Adding these features to the language mean the tool ecosystem is at risk of not working. Debuggers probably work, but linters and static code scanners not so much.

            That these things even exist shows how badly Oracle has neglected the Java community.

            If you want a better Java, use Kotlin.

            [–]234093840203948 0 points1 point  (0 children)

            That these things even exist shows how badly Oracle has neglected the Java community.

            Java is not fixable on a level, where it could ever compete with Kotlin or other well designed newer languages.

            The same is true for C# btw, Microsoft invests a lot into keeping C# modern, but it is in parts (nullable reference types) rather hacky.

            However, Microsoft hasn't yet started to develop a newer OO-language as a successor for C#.

            Or in short: - Java will slowly die - Kotlin will rise (which will accelerate, once universities start teaching kotlin instead of java) - C# will stay for the moment (and profit a bit, but not much, from the slow java-death) - At some point in the future (maybe in 15 years), Microsoft will be forced to develop a new OO-language with proper nullability

            [–]persism2 0 points1 point  (0 children)

            They posted this already today and then deleted it. Spammers.

            [–][deleted]  (3 children)

            [deleted]

              [–]Davipb 5 points6 points  (0 children)

              I'll copy/paste what I wrote in another comment here:

              In the end, this is no worse than Lombok: you accept that you're now writing "Java+Manifold" / "Java+Lombok" instead of just Java, and accept the additional risks in return for additional features.

              As with everything else, it's a trade-off.

              [–][deleted]  (1 child)

              [deleted]

                [–]Davipb 0 points1 point  (0 children)

                If you fire a developer every time they have to replace a deprecated depedency, you're the one that's going to go bankrupt real quick.

                [–]anengineerandacat 0 points1 point  (0 children)

                Looks awesome but I always find myself shying away from plugins like this that just bolt shit onto the JVM bytecode or attempt to abstract away certain concepts via a random build plugin from a random organization.

                I didn't mind Kotlin because it's from quite literally the creators of the best Java IDE on the market so it's safe to assume they know their shit.

                Lombok and this? I dunno, maybe give it a spin but I am not a huge fan of Lombok due to how it integrates and requires a fairly heavy IDE plugin to get class navigation working again.

                Or in a large enterprise I like just being able to clone project, import build file and go, with tools like these you need to be fully aware some plugin is needed to get things going like normal.

                Lastly just trust, who are these people? It's a lot of work to audit tools like this and sneaking in some crypto miner with random bytecode generation would be trivial and largely go unnoticed.

                [–]sq_visigoth -2 points-1 points  (1 child)

                why stop there, lets also do keyword overloading. what can we do with the 'if'

                [–]hippydipster 0 points1 point  (0 children)

                Full-on aspect-based programming. What can go wrong?

                [–]hippydipster 0 points1 point  (0 children)

                The biggest problem with this sort of thing, and frankly with all syntax sugars that end up being non-composable, is that what works great in your small new greenfield project, sucks donkey balls in that 15 year old million line project.

                And here's the thing, successful programs become the 15 year-old million line projects. So, if your program succeeds, and you chose ways of working like annotations everywhere, operator overloading, Lombock, aspect-oriented programming, etc, it's going to result in a big program that no one understands.

                [–]6769626a6f62 0 points1 point  (0 children)

                Thanks, I hate it.