2 ms·
I think things become a lot clearer when you unpack which APIs those "some APIs" are. The case where sum-typed return types (because that's what we're really t
by derefr 4y ago
I think things become a lot clearer when you unpack which APIs those "some APIs" are.
The case where sum-typed return types (because that's what we're really talking about here) truly shine, isn't in handling "errors" in the sense of invalidated preconditions that you need the caller to handle.
Rather, the case where sum-typed return types make sense, is in handling condition states more generally.
When I say "state" here, think "finite-state machine." Or actually, maybe think "pushdown automata." Imagine a sum type representing the state of a door, like:
Open | Closed(door_tag: DoorTag() | None) | ClosedAndLocked(required_key_bidding)
If you call door.try_open(), it makes perfect sense to return this sum type. This type represents the new state of the door, after your attempt. None of the types are exceptional per se, because this is just an attempt to open the door. You're equally happy to have the door in any state afterward. "Open if you can", per se.
Certainly, you could write another function door.open() † that does throw an exception if the door cannot be opened. It might also raise a DoorAlreadyOpenException. But this function makes more sense to implement on top of the first function, than the other way around. Door.try_open is more core to how the door models its state and reacts to input, than Door.open is.
† In other languages, the naming-complexity expectation would be reversed — the non-raising variant would be `Door.open`, while the raising variant would be something like `Door.open!`.