Java's Original Sin (2009)
gbracha.blogspot.com
gbracha.blogspot.com
The work on that project has been impressive, and there is now an experimental EA build called LW2 where you can play with value types: https://wiki.openjdk.java.net/display/valhalla/LW2
Will this solve everything? No. The char type is hosed. But there will be an opportunity to e.g. get a new Character.
They learned from Java's terrible sins. Features include value types (you can even use them as generic params!) And no type Erasure.
Besides massive improvements the language is nearly identical to Java. Only reason I'm still in Java land is the huge ecosystem.
Making characters objects doesn't seem like it magics away this issue. You'd need APIs to get and set raw values somewhere, and those APIs would have become the problem.
You never need to "get and set raw values". You need APIs to do the things that are sensible to do with characters, such as "test whether it has this property" or "encode as UTF-16 byte sequence", but all those things have much clearer semantics than "access raw value".
def to_string(s)
s.split(", ").map { |dec|
dec.to_i.chr(Encoding::UTF_8)
}.join
end
"100, 8217, 65, 118, 111, 114, 105, 111".to_string
=> d’Avorio
I had to specify the encoding because an unassisted Fixnum#chr works only for ASCII values. However I can imagine that it could use some intelligence to figure out the correct encoding. Probably it's not worth it and it could make mistakes.You could convert to a different character format but at some point you need raw values. If you want to be able to read text created by something else (text editor, browser, different OS) you have to expose raw access.
You need to encode as bytes with a particular encoding. That's not the same thing as "getting the raw value".
> And for searching (it's hard to build indexes in Unicode because of uneven symbol widths).
It may be hard but it's important to do it right. If searching for "café" doesn't find "caf◌́e" then you've got a problem.
> You could convert to a different character format but at some point you need raw values. If you want to be able to read text created by something else (text editor, browser, different OS) you have to expose raw access.
Again, no. You need to be able to decode byte sequences that have particular encodings into strings of characters. That doesn't mean you need to expose the internal representation of those strings/characters.
You keep reiterating this, but it's not feasible. To avoid exposing the raw values, the language would need to support all possible encodings.
A simple proof of this is that you can write custom character sets in Python too, even though there's no way to have raw byte access to a Python character (because it's different on different platforms/builds).
There are no raw values.
The same string, used in Qt/C++ vs. stdlib/C++ results in different raw values, because it’s encoded and handled differently.
The fact that you believe there is a single true "raw value" of a string already shows why it’s important to make this explicit, because your code will break if used in different environments, and definitely won’t return the correct result.
And, of course, users may need to convert their own binary formats to strings (compression and the like). At some point you'll be dealing with raw values.
Please let me know what to think dang.
Edit: I noticed the 'past' link, but that takes you to the search engine. I'd prefer something more direct like what @dang just posted.
Remember this decision was made in early 90's and getting users onto the language was imperative. Target audience was most likely using C at the time.
I have been bitten more than most by the 16-bit char. I completely agree with the modern popular opinion that strings should be made from grapheme clusters, or failing that, codepoints.
But I get confused when the author uses the example of int, and then we run into problems. One confusion I have is how the author says that a method naming scheme is not operator overloading, when actually that's how it is always done in the languages I can name. Another confusion I have is that if primitive int and Integer merge into Int, then we have problems with null?
I've been a bit involved with the Mill CPU effort, and lets not go off on a tangent about that, but one thing in there that is a thing of beauty is the Not-A-Result/None approach. Values have special meta-data to say if they are an error or not. So, say, dividing by zero returns a value, but that value is tagged as an error, rather than an exception being thrown. If you use this value in subsequent computation, then the error propagates. Only if you then try and do something externally visible will the error throw. 'None' is a special type of error that doesn't throw when it becomes invisible, but instead becomes a no-store. This model is excellent for the speculation inside CPUs, but I posit that it would also be a really awesome model for higher level languages too.
Imagine modelling null as 'None'; a function returns a number, except it returns None instead. This None is then used in subsequent math, and the Noneness propogates. When you try and do something with it, it just silently does nothing. Another time a function returns an overflow error, or something like that, and when you eventually try and write that to a member variable where it becomes visible to other code, there's an error thrown. I know first hand that when you internalize this, lots of error handling and checking evaporates and its a nice zen.
Personally, I wish I had a faster and statically typed Python. I love Python's infinite integers and its string handling (which I had no problem with in python2 either, so ymmv). My biggest gripe with Python in production is that it isn't statically typed.
The author isn’t suggesting that this would be a design change that would be compatible with the current semantics of Java, so behaviour for null would change.
I can think of two main uses for Integer: when you have type polymorphism (e.g. my own code often has maps of Object) or nullness.
I go on to describe a different approach to nullness that suites ints, for example.
With mypy (http://mypy-lang.org/), it could be.
I think the author is drawing a distinction between allowing custom operators with custom precedence, versus allowing custom implementation of a fixed set of operators with fixed precedence. I'd agree that it's a poor choice of terminology, since we would generally call the latter "operator overloading".
> Personally, I wish I had a faster and statically typed Python. I love Python's infinite integers and its string handling (which I had no problem with in python2 either, so ymmv). My biggest gripe with Python in production is that it isn't statically typed.
Yeah, I loved Python until my codebases became too large to refactor safely. I've found Scala gives the best of both worlds - the mixed object/functional style is quite Python-like and the low-ceremony syntax can be used to look quite Python-like (if you avoid libraries that use a bunch of symbolic method names) - though I wish infinite integers were the default there too. (I can write literals as e.g. bi"123789890897094790" and use normal operators on them, so it's not the worst).
Ever tried Crystal? It is more a statically type Ruby, but perhaps it would be close enough.. It don't have integers that automatically expand to bigger sizes though - it is in the process of at least getting errors on overflow on the other hand.
It also don't follow your idea for the None type, but instead have union typing, meaning that a nilable integer has a type Int32 | Nil (assuming 32bit size of the integer).
It's disconcerting and astonishing to check for true / false and get a NPE, to say the least.
One nice thing about Go is that strings are values and cannot be null.
Edit: In Python, equality, addition, even attribute setting can all be overridden by implementing certain "magic" methods on a class. Much of Python syntax is effectively syntactic sugar for calling these methods.
Go runes are codepoints.
I think Swift is interesting, a "character" in Swift is actually a grapheme cluster.
(You'd see objective-c benchmarks suffer because of this)
I just wrote my first Go program this past weekend, evidently I misunderstood the tutorials/docs I read.
Notice how big a problem the string/bytes split in Python 2 -> 3 migration was though - probably bigger than anything Java has had to do - which suggests this wouldn't necessarily have solved all problems as the author would like.
By what measure, if not this one, can we truly compare languages?
He's also one of the team behind Dart. Hrm.
Consider that term in it's original context, and the size of the human population. It's clear that "original sin" does not imply failure to thrive.
While the ideas are (somewhat) valid, it would have been a quite different language from the language that gathered traction and became popular as Java.
Yep. Practicality beats purity when coding in industry, and the Java ecosystem has almost always chosen practical solutions over ideal ones.
This is the intentional result of prioritizing practicality and usefulness and portability.
People who write articles complaining about Java seem to think Java should have different priorities, but Java has been one of the most popular languages in industry for a long time because its priorities make sense.
The description above is more of a strawman than the title is clickbait.
There's nothing in the post that would make Java less "understandable & popular for developers from C and similar backgrounds".
If anything, the simplifications proposed would make it more coherent and understandable.
>While the ideas are (somewhat) valid
What part of the ideas you don't find valid?
>it would have been a quite different language from the language that gathered traction and became popular as Java.
Not in any substantial way except that it would have more consistency in the treatment of value types, and 90% of it behind the scenes. Everything else, from a syntax, language, and API design perspective would be the same.
(Java with Generics, streams, closures, etc. today, and Value Types soon, is widely different already from the Java of 1997).
class FirstName extends String {...}
I of course can uplift my String into Objects. But it is unneeded for 90% of the String usages. Plus it forces you to unbox the String from its Object to pass it to a function that consumes just a String.
I'm actually curious to hear why the author thinks this, because I've largely come to the opposite conclusion…
The OO system is absolutely weaker. Depending on your situation, that can be a big advantage. I work in a mixed Java/Scala codebase, and, while I generally like Scala, one of our more annoying bits of yak-shaving is making sure outward-facing Scala code doesn't do anything to make itself overly awkward to consume from Java. I haven't worked as extensively in Kotlin, but, from what I've seen, you've got to spend a lot less time worrying about it there, because Kotlin stays much closer to Java's OO semantics.
My sense is, if type erasure or lack of parametric polymorphism are really causing you pain, yes, Scala is for you. Pattern matching, too. If your Java pain points are more prosaic than that, Kotlin is a less radical change that is likely to address most of them them without introducing too many new ones.
But this actually would have been better in Java because the compiler could have delt with the inefficiency cause by the unnecessary overhead.
At implementation make it an object but at runtime it will be a scalar.