6 ms·
I don't really dislike the concept of checked exceptions, but the awful way they prevent the usage of functional interfaces and modern Java in general is pretty
by gordaco 6y ago
I don't really dislike the concept of checked exceptions, but the awful way they prevent the usage of functional interfaces and modern Java in general is pretty infuriating. Functional interfaces and code that uses them should have a generic way of being transparent to exceptions, but I'm not sure it can be done without breaking the language.
Nevertheless, I still believe that Java's biggest mistake is not checked exceptions, but the stupid distinction between primitive types and Objects (caused because all the cruft present in the latter would have make basic operations prohibitive in terms of performance, at least for early Java versions) and all the associated boxing. I have seen some extreme cases of performance degradation because of that (fortunately a refactor to use arrays solved the problem, but this is not always possible).
- mrkeen 6y ago> Functional interfaces and code that uses them should have a generic way of being transparent to exceptions This irks me too. It's been a while but I believe the workaround is just to define your own (checked) FunctionE, SupplierE, ConsumerE functional interfaces. But maybe that causes other problems I've forgotten. > the stupid distinction between primitive types and Objects I also dislike the distinction between primitives and Objects. But I don't think your argument follows. Performance is why the distinction exists. Objects have the cruft and primitives don't.
- tpxl 6y ago>This irks me too. It's been a while but I believe the workaround is just to define your own (checked) FunctionE, SupplierE, ConsumerE functional interfaces. But maybe that causes other problems I've forgotten. You can do that, but you need to do it for every exception type ): I'd love it if Java had templated exceptions. >> the stupid distinction between primitive types and Objects One of the slowest things I found out about C# was floats being objects and `a < b` (or something similar) being a stack about 7 levels deep.
- gordaco 6y agoThat's right, I would run into even bigger performance issues if I used Integer/Long/Double/etc everywhere instead of the primitive types. The problem is not that primitive types are not object, but rather, that generic code will only accept objects and not primitive types.
- srtjstjsj 6y agoSo don't use generic code in critical inner loops.
- ossopite 6y ago> You can do that, but you need to do it for every exception type ): I'd love it if Java had templated exceptions. It does have them. You can write an interface with a type parameter `<E extends Throwable>` and declare functions inside it with `throws E`
- culturedsystems 6y agoYou can do this, but it's not ideal. A function can list multiple exceptions in its throws clause, which share no common type except Throwable, but this combination of exception types can't be represented by a single type parameter (except for the common base type). So in a lot of cases, you lose information about the specific exceptions your function can throw, which limits the value of using checked exceptions.
- toolslive 6y agoThe other alternative is to use the error monad in combination with railroad style programming. Of course you also want infix operators like (>>), (>>?) aso.
- jasonboyd 6y ago> One of the slowest things I found out about C# was floats being objects Floats are value types not objects: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/floating-point-numeric-types https://docs.microsoft.com/en-us/dotnet/csharp/language-refe... Maybe you we're dealing with floats that had been boxed?
- mumblemumble 6y agoPerformance is why you should use different implementation details on the metal. The reason why there's a distinction at the language level, though, is because Java's designers chose to leak implementation details into the language's high-level semantics. This distinction wasn't such a big deal in 1995. It was perhaps even the preferred way of doing things according to contemporary values. Nowadays, though, Java is a very different language that lives in a very different cultural milieu. So, nowadays, the language-level distinction, in combination with some other design decisions that came later, is absolutely a source of performance problems. Any code that tries to do things like store numbers in one of the standard collection classes will quickly devolve into a horrible mess of memory overhead and pointer chasing. It's led to this perverse situation where it's honestly not too hard to beat Java performance in a dynamically typed language like Clojure or Python, simply because dynamic languages make it easy to encapsulate the implementation details (and therefore to choose a more performant run-time implementation) in a way that Java's type system doesn't really allow. You can still keep Java toward the top of the well-known performance benchmark leaderboards, but only by coding as if Java 5 never happened. Which is an option that's only tenable for toy use cases like benchmarks.
- mrkeen 6y agoI don't disagree about the penalties of boxing and chasing pointers and moving things around on the heap. And I reiterate my dislike of the ergonomics of having a primitive/Object distinction, especially when it comes to collections. But I'm skeptical of the argument that the language designers could have improved performance while hiding the implementation details from us. If anything it feels like the reverse is true. Here's a few additional implementation details I'd like access to: * I want to allocate an object (by value - not by pointer) straight onto the stack (not the heap). * I want to specify that a class completely opts out of inheritance so that the compiler knows which exact method I'm calling (static dispatch) and can inline appropriately. * (If tail-call optimisation existed) I'd like the ability to annotate a method as such, and have the compiler reject it if TCO couldn't be done. > It's led to this perverse situation where it's honestly not too hard to beat Java performance in a dynamically typed language like Clojure or Python, simply because dynamic languages make it easier to encapsulate the implementation details (and therefore to choose a more performant run-time implementation) than Java's type system does. I'm skeptical of this too. It sounds a lot like the "Java faster than C++" I've been hearing for like 15 years, e.g. > https://trs.jpl.nasa.gov/bitstream/handle/2014/18351/99-1827.pdf https://trs.jpl.nasa.gov/bitstream/handle/2014/18351/99-1827... > Java performance can exceed that of C++ because dynamic compilation gives the Java compiler access to runtime information not available to a C++ compiler.
- gordaco 6y agoYes, objects have the cruft and primitives don't. So you use primitive types whenever you can, including mathematical operations (unless the numbers are big and you need a BigInteger or BigDecimal, but that's another story). So far so good. But then, because of type erasure, collections (and literally anything generic except arrays) only accept objects, meaning that if you need a set of longs, you use a set of Longs instead. And if you are using some complex algorithm that relies on maps or sets of primitive collections, where you are constantly adding or removing elements, boxing and unboxing will kill your performance. I have been hit by this more than once. The last time I measured this (about half a year ago, which was the last time I encountered this in my job), the difference in memory usage was several Gb, and in particular there were about 7Gb used just for the Long class. There are third party libraries that alleviate this. I use koloboke a lot, for example (which works for maps and sets, but lacks sorted collections and multimaps, which I also need to use very often); but the problem remains for anything slightly complex, and it's not uncommon to find yourself writing two almost identical copies of a relatively complicated method or class, one for objects and another one for ints (and maybe a third one for longs, a fourth one for floats...), because otherwise you hit the same problem. So yeah. Primitive types are not objects and that limits them because Java doesn't work well with things that are not objects. C++ is much better in this sense, because generic code is actually generic and any type will do.
- srtjstjsj 6y agofastutil.di.unimi.it You don't need to rebox in performance sensitive code.
- ragnese 6y agoI think that neither of those are Java's worst offense. It's that all references are allowed to be null.
- zmmmmm 6y agoI think the syntactic awkwardness in general in Java have been a huge issue and it's a shame because people hold it against checked exceptions rather than the language. For example, it took forever to be able to handle two unrelated exceptions with the same catch block, and incredibly common things like closing file handles would require nested try / catch blocks in all the primary catch clauses (and now every utility library ends up with "closeQuietly" or similar ...). I think if Java had done it much better there would be a massively different opinion of CE.