There’s been consistent feedback about how some WPILib DriverStation methods return default data before a connection and how that’s caused issues in teams code. For example: getAlliance() always returning 'Red'
Java 8 and C++17 introduced Optionals which provide programmatic ways to indicate that data hasn’t been received yet. This would be the “modern” way to update the API. However, there are other ways (such as adding an unknown to the enum).
I’ve heard optionals come with (somewhat significant?) extra runtime cost, is this true? I know we already kind of ignore this by choosing to allocate objects constantly in our loops, so guess this won’t be a factor in the decision either.
I believe this video well explains why it should be an optional. If you add an unknown value, the people who don’t read the docs won’t notice it. Whereas if you use an optional it becomes very obvious that it can just… not exist. Compiler time errors are always better than runtime errors, especially when said runtime errors can cost you a $500 match simply because the error is impossible to run into in practice.
I am not a Java programmer, so I have little exposure to Optionals. I did see them for some of the vision code (PhotonVision) and they seemed fine.
However, it does still seem unnecessarily more complicated than adding a “invalid” value to the enum. This has been available since enums were “invented”, and for me, has always been part of my work (ie most enums have a “n/a”, “undefined”, or similar value).
So, my vote would be to extend the enums to have the appropriate “error”/“not valid”/“unknown” values, as needed. Use Optional for non-enums (or just use Null).
Optional<T> is actually a functional programming language construct used for error handling (though not exclusively). It’s the sum type between null and T (i.e., it can either have a value or not). Result<T, E> is another common FP error handling type that I don’t think Java has.
It’s not more complicated, just a different style you aren’t used to. You’d still writing a similar amount of code, but the Optional vocabulary type communicates intent more directly by separating nominal cases from the exceptional case at the type level. “Invalid” isn’t another alliance kind; it’s the absence of an alliance kind.
Why Java’s Optional is bad
With that said, Java’s implementation of Optional is poor. Its performance is much worse than C++ or Rust due to heap traffic, and it not being a proper monad means you can’t do Java 20 pattern matching on it. At least they have member functions like ifPresent() and or().
Here’s more context on optional:
We’ve been seeing garbage collection (GC) pressure from lots of small “value types” like geometry and matrix math, and Optional adds to that. Even for built-in types like Optional, Java just isn’t designed for what we’re trying to make it do (GC is why we can’t have nice things).
Why it probably won’t get better
A modern, low-pause GC could alleviate the GC pressure, but none of them are available for 32-bit ARM (the roboRIO). Control system refreshes generally last for at least 5 years, so the earliest we could change the target platform is 2027.
The Project Valhalla proposal’s value types may help avoid allocations, but past instances of the “we have X at home” meme in Java make me pessimistic. There’s:
Generics
Not reified, so can’t be used for function overloading
It’s a glorified cast to/from Object
Optional
Poor performance
Missing functional features
Destructors
finalize() was a failure that got removed from the language
Cleaner is just deferred destruction with more manual control over when the destruction occurs.
try-with-resources and AutoCloseable are a super verbose workaround for lack of automatic destructors, and escape analysis isn’t perfect