5 ms·
I think of Java as hitting an 'anti-sweet-spot': - It forces us to go through the ceremony and verbosity of static typing, yet blows away lots of the benefit b
by chriswarbo 6y ago
I think of Java as hitting an 'anti-sweet-spot':
- It forces us to go through the ceremony and verbosity of static typing, yet blows away lots of the benefit by pervasive use of `null` and `Object`. Compare this to ML or Haskell, where there's less ceremony (Hindley-Milner type inference) yet more safety (no `null` or down-casting)
- Checked exceptions have a similar ceremony and verbosity problem, yet the pervasiveness of unchecked exceptions prevents us actually having much confidence that calls will succeed. Compare this to effect systems, even rudimentary ones like Haskell's (`Maybe`, `Either`, `IO`, etc.): if a function doesn't return one of these 'effect actions', we can be pretty sure that it will succeed (the only unchecked exceptions I've encountered in Haskell are pretty catastrophic things like OutOfMemory).
- It needs to be compiled ahead-of-time, which requires more tooling, makes automated testing more annoying (we have three different failure-reporting mechanisms: app compile error, test compile error, test failure), tools like REPLs are second-class afterthoughts, etc. Yet the resulting bytecode still requires an interpreter (JVM), which takes a while to spin up, and makes deployment and packaging harder (we don't have a self-contained binary).
- It pushes an OOP style, but requires that we keep subverting it; e.g. pervasive use of using structured-programming constructs like `for` (which other OO languages like Smalltalk avoid in favour of method calls), which requires we break encapsulation/implementation-hiding (e.g. if we loop with an 'Iterator', we need to know whether anything we call will use that same Iterator).
- It seemingly pushes a high-level, highly-abstracted design (methods calling other methods, classes maintaining their invariants, etc.); but then throws threading into the mix, which breaks things into such low-level pieces that even 'i++' needs to be thought of in terms of constituent parts (read, increment, write, return).
I agree that Scala's better (e.g. reducing a lot of the typing ceremony; making it easier to remain high-level/abstracted (e.g. encouraging 'map' and friends rather than effectful loops), etc.); it inherits some of Java's problems (and doesn't even flag checked exceptions!), but it's nice enough to tilt the balance in many cases.
- twic 6y ago> pervasive use of `null` and `Object` Who is pervasively using Object? You are talking about a language that hasn't existed for a decade.
- chriswarbo 6y agoAs a simple example, `x.equals(y)` accepts an arbitrary Object y. This one example has ramifications all over the place, e.g. I had a bug recently where I was checking if an element was in a collection, using `x.contains(y)`. That .contains method seems to use .equals, since it allows y to be any Object. In my case x was a collection of MyType values whilst y was a MyWrapper<MyType> which I forgot to unwrap. Rather than giving a type error, this check was always returning false. Since then I've defined a .has method which requires the argument to have the same type as the collection, which completely avoids such problems.
- twic 6y agoExcellent example, thank you.
- sixbrx 6y agoYes and also Map's "get(Object key)" and "remove(Object key)". Those are the bane of refactorings, for me.
- blandflakes 6y agoFun fact, this also happens in Scala - try to List.contains an object from a type different than that stored in the list. Compiles, returns false always.
- chriswarbo 6y agoI actually encountered this in Scala (my example originally had [square brackets] rather than <pointy ones>). My `.has` method uses cats.Eq and is monkey-patched on to the Iterable interface: implicit final class IOps[A: cats.Eq](val i: Iterable[A]) { def has(x: A): Boolean = i.exists(_.eqv(x)) }
- blandflakes 6y agoHa, yup. We use the .contains_ method with cats eq on the objects, but this is clever.
- Yeroc 6y agoNot really following or agreeing with most of your arguments here but on the subject of Checked/UnChecked exception I agree. Java would have been better off with simply Checked exceptions and errors and no RuntimeExceptions. The current lovefest for unchecked exceptions in Java is very unfortunate. New features like lambdas don't work well with checked exceptions and there seems to be little urgency in rectifying those shortcomings either. Instead everyone is just switching things to use unchecked exceptions. For people running stuff in web containers I suppose it hardly matters, a single request fails, no big deal but for other types of programs having checked exceptions is invaluable.