Programmers have a tendency to write functions that take types that are too generic for what the functions actually want.
The most common form of this is called Boolean Blindness
where a function takes boolean arguments that don't actually say what they want. An example that comes to mind is widget.paint(true);
. It's impossible to say what that boolean parameter does without looking up the documentation. The general solution is to either take an enum
(short for enumeration) or an approximation thereof. It'd look something like widget.paint(WidgetPaintMode.Immediate);
.
In generic this is called Primitive Obsession
. Functions that take integers, booleans, strings, and other highly general types. And the solution is to create a more specialized type, either by wrapping the type in a wrapper or creating a new type type to represent the concept, ideally making it impossible to represent impossible values.
Programmers also have an error handling strategy where they return a type that represents either a value or an error. In many languages, this type is called Either<Left, Right>
This Either
type can either be an instance of the Left
or an instance of the Right
, and it knows which instance it is at runtime. You create one with Left(a_value)
or Right(a_value)
.
I believe that this usage of the Either
type for error handling is a form of primitive obsession. We are using a generic type (something that represents either one type or another) for a specific purpose (something that is either a successful value or an error thrown by an operation). We should be using a specific type for this concept. And in the Rust programming language, we do.
This type is called Result<Ok, Error>
. Instead of creating it with Left(a_value)
and Right(a_value)
, you create it with either Ok(value)
or Error(an_error)
. If you're looking to return a proper type that describes an operation that can fail, this is the type I think you should use. It is more evident what is being returned, and doesn't depend on convention on which variant is the erroring one. It's obvious when you've made a mistake, and people who've never used the type before can understand what it represents without having to read documentation.