Pre-SIP: A New Type for Optionals and Error Handling (2)

The issue of handling missing / alternative values with Option and friends has been a source of disappointment for quite some time, especially if we look at the alternatives other modern languages provide. The community has been throwing libraries and proposals at it (including my own proposal to empower T | Null) but it never felt like we nailed it.
And despite the grass being greener on the neighbours side, IMHO no programming language managed to provide a fully unified and satisfactory solution so far.

This proposal is really refreshing: it’s ergonomic, holistic and innovative. In short, the true spirit of Scala ! And I’m sure it’ll serve as a lighthouse for other languages as has been the case with a several language features in the past.

While we’re at it, we have an opportunity to take some of the best productivity features and sugars programmers love that have proven their worth across popular languages. It’s great that the postfix ? operator has already been proposed, here are some other additions that come to mind:

Feature parity with Option

fold, get, contains, exists, … All the familiar methods we love from Option (and maybe others ? The Rust API could be a good source of inspiration) would make the feature more ergonomic and help with the transition.

?? AKA nullish / nil coalescing operator

A great companion to the ? operator. Languages which have the latter usually provide the former.

def isItGreat(input: String?): String = input ?? "Absolutely!"

Available in JS / TS, Swift, C# and Kotlin (as ?:). Functionally equivalent to getOrElse but shorter and probably clearer.

Aborting to the enclosing function by default

Could we have the best of both worlds ? maybe is great when one needs that flexibility, however the most common case will probably be Rust-like, i.e. aborting to the enclosing function. Would it be feasible / desirable to have that as the default (given that the return type matches) ? This obviously would spare us some ceremony and syntax overhead (especially for one-liner functions) but mostly would make the feature easier and more accessible to beginners.

I think this would get ambiguous too quickly, since we also allow code that omits the maybe but passes a CanErr parameter (in which case the abort would go to the caller). In terms of notation it’s not more verbose than Rust since Rust demands a wrapper for the result. So if we adopted Rust’s scheme we’d write:

def f = 
  ...
  Ok(result)

whereas in this proposal it is:

def f = maybe:
  ...
  result

It’s the same number of calls in the end.

2 Likes

So I note that JEP 401 is now in Java 28 as a preview feature. Is there anything in this feature that would likely preclude future integration with Java value classes?

2 Likes

Still digesting this, but from a usability standpoint, I’m mixed. Its clean, and I like the explicit T ? Err, which is less cluttered and clearer than Either (and the order doesn’t feel backwards). I worry that T? meaning T | null sends the message that ā€œnulls are ok nowā€, after years if steering users away from them—and Scala being relatively free of NPEs as a result. Maybe you save the Option wrap drama but you still need the match on result. Will the compiler warn you if you forget to match and blindly try to use result T?

3 Likes

That’s why this only makes sense when paired with Explicit Nulls. As I understand it, Explicit Nulls neuters the traditional problem, which implies that it is now possible to use null in productive ways.

2 Likes

But that’s why it also shouldn’t be used for any serious library at this point (or soon), because only a subset of the ecosystem uses explicit nulls. If we deprecate anything but explicit nulls, then sure, use ?. But otherwise it should stay niche.

Or, alternatively, it needs to make sense without explicit nulls. Introducing a new decoration T! would do the trick, because then T for a type not provable to be disjoint from null would act akin to T | Null, T! would act like T, and T? would turn T into T! or whatever the sentinel is (which might be a no-op). So all the key pieces would be isomorphic, more or less, just accessed differently.

1 Like

I agree this cannot go in before explicit nulls are standard. There’s hopefully going to be a SIP soon for explicit nulls.

Right now, the implementation requires -Yexplicit-nulls to be set for ? to be parsed without error.

6 Likes