5 ms·
> No unsigned ints. I think that's a mixed blessing. I believe Java did this deliberately to avoid the trouble that C and C++ have with signed and unsigned int
by MaxBarraclough 6y ago
> No unsigned ints.
I think that's a mixed blessing. I believe Java did this deliberately to avoid the trouble that C and C++ have with signed and unsigned integer types having to coexist. Personally I've never been inconvenienced by Java's lack of unsigned integer types, but I'm sure it can be annoying in some situations.
I'm quite fond of Ada's approach to integer types, but I suspect I'm in a minority.
> Silent integer overflow/wrap-around. It's not C- did it really have to copy this insanity?
Curiously this cropped up 10 days ago. [0] You're not alone. The great John Regehr put it thus: [1]
> Java-style wrapping integers should never be the default, this is arguably even worse than C and C++’s UB-on-overflow which at least permits an implementation to trap.
> The fact that arrays got type variance wrong.
At least Java has the defence that they didn't know how it would pan out. C# has no such excuse in copying Java.
> No concept of `const` or immutability.
I recall a Java wizard commenting that although a const system is the sort of feature that aligns with Java's philosophy, it's just too difficult to retrofit it.
[0] https://news.ycombinator.com/item?id=26538842 https://news.ycombinator.com/item?id=26538842
[1] https://blog.regehr.org/archives/1401 https://blog.regehr.org/archives/1401
- int_19h 6y ago> I believe Java did this deliberately to avoid the trouble that C and C++ have with signed and unsigned integer types having to coexist. The problems really only come from mixing those types, and the simple solution is to disallow such mixing without explicit casts in cases where the result type is not wide enough to represent all possible values - this is exactly what C# does. I think Java designers just assumed that high-level code doesn't need those, and low-level code can use wrappers that work on signed types as if they were unsigned (esp. since with wraparound, many common operations are the same). > Java-style wrapping integers should never be the default The ironic thing about this one is that C# introduced "checked" and "unchecked" specifically to control this... and then defaulted to "unchecked", so most C# code out there assumes the same. Opportunity lost. While we're on the subject of numeric types - the other mistake, IMO, is pushing binary floating point numbers as the default representation for reals. It makes sense perf-wise, sure - but humans think in decimal, and it makes for a very big difference with floats, that sometimes translates to very expensive bugs. At the very least, a modern high-level language should offer decimal floating-point types that are at least as easy to use as binary floating-point (e.g. first-class literals, overloaded operators etc). C# almost got it right with "decimal"... except that fractional literals still default to "double", so you need to slap the "M" suffix everywhere. It really ought to be the other way around - slower but safer choice by default, and opt into fast binary floating-point where you actually need perf. > At least Java has the defence that they didn't know how it would pan out. C# has no such excuse in copying Java. I think both Java and C# did it as an attempt to offer some generic data structure that could cover as many use cases as possible, since neither had user-defined generic types. In retrospect, it was an error - but before true generics became a thing, it was also a godsend in some cases.
- ragnese 6y agoMy opinion is that a high-level language like Java has no business making me guess how many bytes my numeric values will occupy. It's insane. Since when does Java give a crap about memory space? "Allocations are cheap!" they said. "Computers are fast!" they said about indirection costs. Then they stopped and asked me if I want my number to occupy 1, 2, 4 or 8 bytes? Are you kidding me? Yes, you should have those types available so that your Java code can interact with a SQL database, or do some low-ish level network crap, or FFI with C or something. But the default should basically be a smart version of BigInteger that maybe the JVM and/or compiler could guesstimate the size of or optimize while running. Thus, IMO, there should be a handful of numeric types that are strict in behavior and do not willy-nilly cast back and forth. Ideally you'd have Integer, UInteger, PositiveInteger, and a similar suite for Decimal types. Schemes have done numbers correctly since basically forever.
- int_19h 6y agoOP mentioned "Ada's approach to types", as well. Ada lets you write stuff like "T is range 1 .. 20" or "T is range -1.0 .. 1.0 digits 18". This then gets mapped to the appropriate hardware integer or floating-point type.
- ragnese 6y agoYeah, I've read little snippets like that from blog posts and stuff, but I've never written a single line of Ada, so I really don't know how that works out in practice. What happens if you overflow at runtime? A crash, I assume/hope? My point of view is that this is the opposite of what I'm talking about anyway. Java is a high level language where we are usually writing in Java because we're agreeing to give up a lot of raw performance (heap allocations, tons of pointer chasing) in order to have convenient models (objects) and not have to worry about memory management, etc. In light of the above, I don't see why the default for Java is to have these really nitty-gritty numeric types. I don't want to guess how big a number can be before launching my cool new product. Just like I don't use raw arrays in Java and have to guess their max size- I just use List<> and it will grow forever.
- ragnese 6y agoI don't know about Ada, but I enjoy Rust's strictness when it comes to numeric types. > Java-style wrapping integers should never be the default, this is arguably even worse than C and C++’s UB-on-overflow which at least permits an implementation to trap. EXACTLY. It's f-ing stupid. C's excuse was compilers doing magic on UB or whatever. Java has no such excuse. They just wanted it to behave the same as C/C++ to attract C++ devs. > At least Java has the defence that they didn't know how it would pan out. C# has no such excuse in copying Java. My understanding was that they DID know it was wrong and chose to do it anyway because it was more convenient and ergonomic to allow it that way. I guess they realized that was a terrible idea, because the generic collection interfaces do it correctly. I don't see how const and immutability align with Java's original philosophy of being object-oriented, which is all about opaque objects that control internal mutable state. The very fact that it's taken until now to have records is proof-positive that "everything is an object" was taken pretty literally for most of its life. Immutable data doesn't really jive with that.
- tom_mellior 6y ago> EXACTLY. It's f-ing stupid. C's excuse was compilers doing magic on UB or whatever. Java has no such excuse. They just wanted it to behave the same as C/C++ to attract C++ devs. But... as you yourself are saying, Java's behavior is not "the same as C/C++". Java wraps while in C and C++ signed overflow is undefined. (Interestingly, C++ is now moving away from UB for this, and defining wrapping semantics. While I'm not one for proof by authority, it looks like some very well-informed people disagree with you about the usefulness of this feature.) Signed integer overflow checking can be almost free. Until it isn't, because it doesn't play nicely with SIMD code. So the code you want to run fastest will pay the biggest price. This article is from 2016 so take it with a grain of salt, but it looks like this can cause 20% to 40% slowdowns: https://blog.regehr.org/archives/1384 https://blog.regehr.org/archives/1384
- ragnese 6y agoI understand that there are performance implications. But a 20% to 40% slowdown for number crunching in a language that is primarily designed for writing super indirection-heavy, heap-allocation-heavy, application architectures is just nothing. Having some kind of high performance math section of the standard library would be fine. But the default behavior is, frankly, dangerous. And for a 20% speed up on operations that are probably far less than 1% of the typical Java application?
- pjmlp 6y ago> I believe Java did this deliberately to avoid the trouble that C and C++ have with signed and unsigned integer types having to coexist. It did, from http://www.gotw.ca/publications/c_family_interview.htm http://www.gotw.ca/publications/c_family_interview.htm > For me as a language designer, which I don't really count myself as these days, what "simple" really ended up meaning was could I expect J. Random Developer to hold the spec in his head. That definition says that, for instance, Java isn't -- and in fact a lot of these languages end up with a lot of corner cases, things that nobody really understands. Quiz any C developer about unsigned, and pretty soon you discover that almost no C developers actually understand what goes on with unsigned, what unsigned arithmetic is. Things like that made C complex. The language part of Java is, I think, pretty simple. The libraries you have to look up. Since Java 8, the standard library has unsigned manipulation arithmetic classes, though.
- MaxBarraclough 6y agoInteresting, here's one of the methods: https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/Integer.html#divideUnsigned(int,int) https://docs.oracle.com/en/java/javase/15/docs/api/java.base...