I should chime in, as I have a large codebase (~45kloc) that has been using explicit nulls since it was introduced (through all its iterations and numerous bugs) and for many years (7 years now), my project is an FRP facade on top of java’s Swing, Qt, Skia, vulkan and web, which uses nulls for a ton of things with semantic meaning.
I second everything said by sjrd here, specially the understanding of principle of least power.
When I wanted optional parameters (and all my widgets have a 20+ parameter list of optional arguments sometimes with defaults), I’ve found that I rather have a
type Opt[A] = A | UnsetParam.type
object UnsetParam
with a small api to use, than using Option or | Null, since swing has specific meanings for null which are different from user unspecified. As mentioned, something like scala-unboxed-option would work here but since the entire api for this is like 10loc I didn’t bother.
In practice, you never want A | Null beyond the field that does store null, you always want to unwrap as soon as possible. I have, over the years used several extension methods to work with nullable types, and now a days, I basically only really use 2 or 3 extension methods:
nullFoldwhich is exactly like Option’s fold but forA | Null(all inlined as you’d expect) andunnwhich is the same asnnbut without the check, since there’s not a single time where I’ve found the defaultnnto make sense, I am already doing the check somewhere else and there’s no situation where I want to discard the nullable part while doing a check that throws a worse NPE (the default JVM will produce a better NPE with an error description showing the dereference that failed).
As you can imagine, nullFold is called like that to be explicit and differentiate it from other folds that might be available due to imports.
The 3rd extension method that I’ll occasionally use is the probably awfully named ? that maps the A in the A | Null. I feel there’s no good name for this as map feels wrong. In fact this method is so awkward that it never feels nice to use, but sometimes (seldomly) I really want to transform T | Null to U | Null.
What I think would really improve usability is to add nullFold, fix nn and do something about vars, because the flow typing support is cute and I’ve effectively used it 0 times. That’s how useful it is as it’s currently implemented
.
Other than this, you don’t need anything as you don’t need to propagate | Null anywhere, it won’t give you a realistic performance boost and it will make your codebase harder. Better to have a custom 0 value like I did above for the UnsetParam