Hacker News new | ask | show | jobs
by wfleming 3435 days ago
Maybe I'm wrong about how it works, but I feel that Optionals in Java are a bolt-on solution that only solves half the problem that the same concept solves in Swift (or Haskell, or Rust, etc.)

An Optional in Java is just another value, and nothing in practice stops that from being null. I.e. a method that returns an Optional can still return an actual null if poorly implemented. So the major benefit Optionals give you in Java is a typed declaration that the method may return an "empty" value, which is certainly helpful for knowing what cases your client code needs to handle, but it does not guarantee that the method will never return null. It reduces the probability of unhandled null pointers, but it does not eliminate them.

On the other hand, Swift & co. guarantee at compile time that a function which says it returns an Optional will always return an an Optional, never a null, and, better still, if a function says it returns some non-optional value, it still is guaranteed to never return null.

It's great that Java is pushing for this style of code, but I'm afraid without compile-time guarantees of return values it will always feel less powerful there.

2 comments

I completely agree with your point -- you can't compare the power of compile-time checking to Java's Optionals, for the most part.

However I tend to think that what's important about Optionals is the spreading of the notion of thinking critically about failure modes around improper input/output. "Defensive" coding is often considered a mid-range skill (at least I think), and IMO it's because a lot of junior programmers just assume if some method get ObjectThing a, it's going to be there, and not null. I think merely seeing Optional<ObjectThing> a encourages movement in the right direction as far as program correctness, a road that will almost surely tour through concepts and languages that take that kind of correctness more (ex. Swift, Haskell).

Also, Optionals are a light introduction to functors which is nice.

For more concrete actionable stuff please see other comment (https://news.ycombinator.com/item?id=13433335), there are tools that can do this at compile time for java

Checked exceptions also forces thinking critically about failure modes, but they seem to be an unpopular language feature, and few new libraries seem to use them. No other popular language has adopted them.

Why are optionals good and checked exceptions bad? (I'm certainly not implying that one replaces the other - i'm just saying that it they both have an origin of forcing programmers to think about errors)

Because checked exceptions are incompatible with the idea of standard interfaces. You can't use interfaces like List, Iterable, Runnable, etc in a world with checked exceptions; you'd have to declare special versions of each of those interfaces for every possible exceptional condition. Since we need standard interfaces to let modules work together, everyone ends up catching and wrapping checked exceptions in runtime exceptions. Any potential value is lost.

Exceptions are exceptional; the whole point is that you aren't supposed to have to think of them in normal program flow. Go gets this wrong too, forcing three extra lines of if err != null { for pretty much every single function call -- and with the added bonus of destroying stack context with each return.

The compiler should be helpful.

I love checked expressions, hate Optionals.

I'll be happy when the language pimps stop trying to make Java look and act like a scripting language.

Edit: I apologize if I've offended anyone with my opinions. I hope this helps. http://bit.ly/1S1943H

https://www.reddit.com/r/java/comments/5dgm96/are_checked_ex...

They generally also break encapsulation (though you can kind of work around/sidestep this by making top-level visible errors more opaque)

Do you have a good reason you'd like to share for liking checked expressions? It seems like the reason you like it is because it's enforced by the compiler -- but I don't think this makes them the right choice, as the compiler could easily also enforce exhaustive handling of an Optional (or sum types like other languages).

If we limit ourselves to the case of Java 1.8, it is a fact that optionals are not checked by the compiler, so there is a solid benefit of using checked exceptions, solely that there is a compile time check of whether the exception was accounted for.

"...a good reason you'd like to share for liking checked expressions?"

Because they're the best solution for the problem. In Java.

The problem with Java's checked exceptions is misuse; turgid overwrought frameworks which obfuscate. Work closer to the metal. Eschew Spring, JPA, Annotations, aspects (AOP). All terrible ideas (making Java more dynamic) implemented terribly. (Quoting myself: Spring is an exception obfuscation framework.)

If you have to catch an exception you can't handle, revisit the design.

Your linked comment references sum types (Rust, Haskell) and multiple return types (Go). I haven't used those, so I can't comment.

The only solution better than checked try/catch/finally might be proper state machines. Experimenting with that is (far down) on my to do list. I'm also curious about Erlang/Elixir (and CSP in general), which uses a completely different strategy for error handling.

*"... Java 1.8, it is a fact that optionals are not checked by the compiler..."

Java's Optionals are a bad idea implemented badly.

The Correct Answer is using the Null Object pattern. Or just use LISP/Scheme. Also good ideas are intrinsic path expressions (vs method chaining) and pattern matching.

---

Top-thread, someone shared the "composition over inheritance" heuristic. This is more correct. Null Object and composition are like peas and carrots.

You mention "breaking encapsulation". So? Choose composition, make most objects dumb, implement the smart/active parts as yet another Interpreter. Problem solved.

> Also, Optionals are a light introduction to functors which is nice.

Optional is not a functor, in fact it violates the functor laws quite blatantly.

I didn't assert they were functors. Just that seeing `map` on something that isn't a list should be somewhat eye-opening to someone who just casually uses it and isn't too familiar with functional programming, that was the point.

Also, when I wrote "functor" I was thinking of https://wiki.haskell.org/Functor, is that what you're thinking of? In that case, which of the rules does it break? if not, what definition of functor were you thinking of?

Optional.of(whatever).map(identityfunction) will definitely give you back an Optional<Whatever>... Am I missing something fundamental? Also Optional.of(whatever).map(f).map(y) is equivalent in return to Optional.of(whatever).map(f . y)... (of course that's not the java syntax for composing functions but I digress)

Consider two functions (excuse my Scala):

  val str: Function[String, String] = s => if (s.length > 3) s else null

  val num: Function[String, Integer] = s => if (s == null) -1 else s.length
With Optional, you receive different results depending on whether you call map twice, or combine the functions first:

  scala> Optional.of("Foo").map[String](str).map[Integer](num)
  res12: java.util.Optional[Integer] = Optional.empty

  scala> Optional.of("Foo").map[Integer](str.andThen(num))
  res15: java.util.Optional[Integer] = Optional[-1]
This is incorrect and violates the functor law.
Use JSR305 with Findbugs or SpotBugs to solve the problem at compile time. Using Optional in Java does have a problem with extra object overhead, so if you want to avoid that, you should use another language like Kotlin or Scala.
Scala's Somes cost an object as well, though (I believe) None is free in both Java and Scala.
You're right. Some is not an AnyVal.