
In Java 3 = 12 - sayham28
http://www.virtualspecies.com/2017/06/in-java-3-12.html
======
wahern
1) Left-associativity

2) The price you pay for type coercion. Be thankful Java defines the order of
evaluation. Not all languages which coerce specify the order of evaluation.

~~~
sametmax
Yes but that's strange. Java is such a staticaly typed language you would not
expect it to do that if you are like me and don't know it very well.

While Python is very dynamic, but:

    
    
         >>> print(1 + 2 + "=" + 1 + 2)
         Traceback (most recent call last):
          File "<stdin>", line 1, in <module>
         TypeError: unsupported operand type(s) for +: 'int' and 'str'

~~~
FascinatedBox
This is possible exactly because Java is statically-typed. Java's type system
works over the operands, and does implicit conversions before the expression
gets a chance to run. It sees values and does implicit magic from left to
right.

Python, on the other hand, does not need to do a typing pass at parse-time.
Instead, literals are stored as values, and those values are tagged with their
source class. Python's way of doing it means that a + operation is called upon
with an integer and a string.

Java might be able to yield the same kind of result as Python if it had
operator overloading. It could define Integer's + operator as only taking
other Integer values, therein yielding a type error at parse-time.

My own language uses ++ instead of + to distinguish between addition and
concatenation, though I've considered some other token since the two are quite
similar to each other.

~~~
sametmax
Actually Python does have a way to change addition semantics. It's just not
defined for this operation.

------
brobdingnagians
This isn't at all surprising if you know how Java works; you could probably
point out this kind of a strange feature case in nearly any language if you
look hard enough.

It does left to right order of operands, so 1 + 2 happens as integer addition
since both operands are integers, then + "=" does string concatenation since
one of the operands is a String, then "3=" \+ 1 evaluates as a concatenation
since one of the operands is a String, giving "3=1" \+ 2, which evaluates to
"3=12" by the same logic. Parenthesis solves this by explicitly specifying
order of operations.

And explicitly specifying order of operations is almost always a good idea for
making sure it does what you want and maintenance purposes.

~~~
Peaker
> you could probably point out this kind of a strange feature case in nearly
> any language if you look hard enough

I disagree. I think it's a product of bad language design.

~~~
reacweb
I disagree. I am not an expert of java language subtleties, but I had no
problem to predict and explain this output. I think it is a product of the
language simplicity. I have a deeper knowledge of C and more experience, but I
regularly fail to predict output in similar exercises.

------
philliphaydon
Does the same in .NET

[https://dotnetfiddle.net/0vkSCV](https://dotnetfiddle.net/0vkSCV)

Used .NET for 15 years and never knew this...

------
danielmg
It's evaluates left to right.

(1+2)=3 then concat with string = then concat with 1 and concat with 2.

The optimiser converts these concats to a StringBuilder.

[https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.htm...](https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.18.1)

Second time I've seen something like this on HN.

------
leibnitz27
I really don't think this should be confusing - having a decent grasp on
associativity and precedence is a pretty useful skill - so for example
understanding WHY println(3 * 2 + "-" \+ 6 * 3) behaves "differently" is
something I'd hope people would get.

And let's be honest, you definitely don't want to 'fix' this by tweaking
precedence to include types. That way madness lies. ;)

Definite shout to ubernostrum's commment though - perhaps the confusing thing
is that string concatenation is +, but a world of explicit Appends seems
horrid, and that ship has very much sailed.

Just be (maybe) glad java doesn't allow generalised operator overloading ;)

~~~
IanCal
I think + for append is fine, it certainly seems to make sense to me that you
get "helloworld" from "hello"+"world".

I think the confusing thing is the type coercion. Does it make sense that
"2"+3 is "23" but 2+"3" is not 5?

    
    
        (3*4).toString() + "=" + 3.toString() + 4.toString())
    

would not be as surprising that the two sides are different.

------
zvrba
I expected to see something more sophisticated than a simple implicit
conversion to string.

------
magnat
And in php (after [https://eev.ee/blog/2012/04/09/php-a-fractal-of-bad-
design/](https://eev.ee/blog/2012/04/09/php-a-fractal-of-bad-design/)):

"foo" == TRUE and "foo" == 0 but TRUE != 0

123 == "123foo" but "123" != "123foo"

"6" == " 6", "4.2" == "4.20" and "133" == "0133", but 133 != 0133, "0x10" ==
"16" and "1e3" == "1000"

------
struppi
I suppose that's something every Java programmer should know: How types are
automatically converted during "arithmetic" operations. There are some
subtleties (like byte+short => int), but it's not too much to remember.

The deeper question is: Was it a wise choice to disallow operator overloading
BUT use "+" as a string concatination operator AND convert all values to
strings during string concatenation?

I think not, but it's a rather moot point, since that desing decision is not
likely to change...

~~~
nicwilson
Thats why I'm glad D chose '~' as a binary concatenation operator so that
there is never any confusion.

~~~
cm2187
There is something to be said about using all the special characters of the
ASCII table in term of readability.

Looking forward languages that will start making good use of Unicode! I am
actually surprised Greek letters haven’t made it already. Lots of computer
scientists are mathematicians that are used to math papers looking like an
Ancient Greek tablet.

~~~
cakoose
The Fortress programming language was a research project at Sun that
incorporated mathematical notation.

See page 24 of
[http://www.oracle.com/technetwork/systems/ts-5206-159453.pdf](http://www.oracle.com/technetwork/systems/ts-5206-159453.pdf)

They also had an ASCII-only representation for everything, so you could use a
regular text editor if you wanted, kind of like Markdown.

------
Insanity
I feel the title is a bit unfair. Technically it is '= + 1 + 2'. Which is
expected as a Java developer.

Coming from other languages maybe it does look a bit odd though

------
lossolo
This was touched literally on second exercises (so second hour of learning
Java) on university when I learned Java.

------
rusk
I had to look twice at this. I was surprised that I could add the string “=“
to ‘3’. I guess what’s happening here is the int gets boxed and then gets
implicitly converted to String. Proof of the pudding would be to try it with a
simple string assignment rather than System.out.println. What happens after
the “=“ is more straightforward

~~~
rusk
This code gives the same result:

    
    
            String splat = 1 + 2 + " = " + 1 + 2;
            System.out.println(splat);
    

So the author's assertion that it is based on some `System.out.println`
compiler magic is false.

The JLS spec [0] accounts for this in its treatment of the '+' operator:

 _" If the type of either operand of a + operator is String, then the
operation is string concatenation."_

This does seem like idiosyncratic behaviour for java to be honest.

I don't think it's the compiler doing it either, because this will throw a
null-pointer exception:

    
    
            Integer borken = null;
            String splat = borken + 2 + " = " + 1 + 2;
            System.out.println(splat);
    

EDIT: Also interesting, if I drop the language level to 1.4 I get this error:

    
    
        Plus.java:8: error: bad operand types for binary operator '+'
            String splat = borken + 2 + " = " + 1 + 2;
    

[0]
[https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.htm...](https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.18.1)

------
Karliss
My question is why shouldn't it be assuming there is no compiler error and
funny pointer arithmetic. If surprising part was lack of error then 1 + "0"
would be a sufficient example. Is left associativity and + operator precedence
not being dependent on data types the surprising part?

------
olliej
This is standard behaviour I would expect in most languages that have implicit
type conversions. The article seems to be willfully disregarding that. I can
think of no language that would not produce either that string, a fault, or
(on the outside) NaN. Java is doing a completely sensible thing here.

That majority of operators are left associative, and that associativity is not
dependent on the types involved.

Very high level let's say the grammar is:

    
    
        Expr :- Expr + Primitive
             |  Primitive
        
        Primitive :- Integer
                  |  String
    
    

And then determining the type is always:

    
    
        type(Integer) = int
        type(String) = string
        type(Expr) = type(Primitive) or minimum_type(left, right)
    

With the simple rule (in this case)

    
    
        minimum_type(left, right)
            if (left == right) return left;
            return string
    

In java the type conversions are done as pass during compile time, doing
something like this

    
    
        for (node in AST)
            if (type(node) != required type)
                replace node with type_conversion_node(node, required_type)
    

But none of this is java specific -- all statically typed languages (with
implicit conversion) would do this. Dynamically typed languages in general
cannot do this (in practice you can do a degree of it with local inference,
etc), but that just changes when the type check is done.

[Edit, because the same confusion about static/dynamic/strong/weak typing is
being made in multiple places.

Strong typing: The language does not allow you to perform an operation on a
type if that operation were invalid when applied to that type. In java (to
make it explicit) I could do (int[])someUnrelatedObject - that cannot be
statically verified but it will fault at run time, because it is strongly
typed. The equivalent in C will happily go off into the weeds and destroy
everything.

Weak typing: A language does not guarantee that invalid operations will be
prevented. For example C and C++ do have types, and they will disallow invalid
uses, but there is no guard to prevent you from casting to an unrelated type.
A more extreme example is some older languages that don't even disallow
argument mismatches, etc.

Static typing: the types in the code are static, specifically they do not
change run-to-run. The types of fields, variables, etc in the source could be
inferred, or explicit by the programmer, but they are all known before the
code is ever run. So C, C++, Java, etc are statically typed. Mostly. Because
of Java's boneheaded array sub typing behaviour Object array[]; array[0] =
someObejct; can fail at runtime due to a dynamic type check.

Dynamic typing: some or all expression types are not known until the code is
run, everyone knows python, js, etc

(getting tired running out of steam)

So anyway, you can have:

Strong/Dynamic: Python, JavaScript, ... Strong/Static: Haskell, etc
Weak/Dynamic: Can't think of a good example here because tired :D Weak/Static:
C

And obviously there's a tonne of in-between

C++: all of C, plus statically type safe casts, and dynamically type safe
casts Objective-C: all of C, but the ObjC object system which lets you send
arbitrary messages to arbitrary targets. But you don't have to have the
correct parameter types... ...

And this all ignores what caused this article in the first place: implicit
type conversions. These do not effect the Strong/Weak description of the
language if they are well defined, eg. people often complain about string
promotion, but neglect to comment on someInt + someFloat promoting the int to
a float, even though such a promotion can lose data. The distinction is purely
a matter of "does the language allow you to perform an operation on a value of
a type that is not compatible with that operation". One way to achieve that is
the say it's completely illegal: throw an exception at runtime, fail to
compile, etc another alternative is to implicit convert the type to something
that is valid. Note that /converting/ a value changes the value being used.
Compare that to the not type safe version where you literally ignore the type
of the value being used. In the case of int + float it's the difference
between converting the int to a float value, and just treating the bits of the
integer as if they were the bits in a float.

Many apologies about the awful writing, but it's late :D

------
yulapshun
I was expecting something more interesting, very disappointed.

------
estomagordo
I think it's mildly amusing that Python produces a TypeError here, while the
altogether stricter Java does not.

------
arnarbi
One of these plusses is not like the other.

------
partycoder
Things to consider here are:

\- operator precedence

\- implicit type casting

\- how operator+ is defined per each type (e.g: numbers add, strings
concatenate)

~~~
Seol
The interesting thing is that the "obvious correct" behaviour would require
the precedence of the + operator to depend on the types of the operands -
which is obviously impossible if it's done at the parse stage...

------
gambiting
So on mobile you can't see the entire width of that website, but trying to
scroll to the left or right changes articles. Absolutely brilliant, why do
people think that overriding the scroll on mobile is a good thing is beyond
me.

------
_pmf_
The explanation is horribly wrong.

------
ramblerman
1 + 2 + "=" \+ (1 + 2)

~~~
usrusr
Or as one might write in real life:

    
    
      (1 + 2) + "=" + (1 + 2)
    

A few braces here and there are a very practical substitute to wasting brain
cycle about subtleties in operator precedence and associativity.

------
pietrovismara
Oh but it's not an article about how shitty javascript is, so let's just
ignore it.

------
essuraj
so does javascript

