ret, err = fd.Write(...)
Above we have two (initial) possibilities 1. The return was error-free, err = nil, and
ret is an interesting return value.
2. The return was errorful, err != nil, err
instead details the reason for error as
an integer, and ret is nonsense.
This is a little challenging because we must be sure to check err in the second case since otherwise ret is silently meaningless. This is a source of bugs called "Boolean Blindness".In a language with Sum types we'd represent this by saying that errorful computations adjoin an error type to the return type.
Int ----> Either Error Int
What's nice is that this automatically ensures the above invariant of err/ret is held: a value of type Either Error Int is either "Right i" for some valid Int i or "Left e" for some error value e. The first thing we check is the handedness of the result and thus know the result of the computation.Sum types like Either solve Boolean Blindness because we literally cannot get access to our return type Int unless it is valid: if we find that the result is "Left e" then all we have is an Error value.
(Ultimately what this boils down to is that product types like (ret, err) behave very differently from sum types and cannot replace them.)
If Go had generics we would also notice that the error-passing behavior of Either is naturally ignorant of the particular choice of types Error and ain't. We could manipulate it meaningfully (and indeed write the functionality used in this article) for any and all values e and r as the type Either e r. Then upon use Either e r would specialize to Either Error Int.
It's easy to argue that this is silly: we only need one error type and it is not difficult to replicate this code. I'll argue for now that only having one error type is at best confusing and at worst a lie. Different processes and functions have different exceptional states. Coercing all of this variation in error meaning into the same error type ensures that misunderstandings and accidental unifications will occur.
Honestly, I really love Go for making sure that errors were values: it is an incredible step forward for making exceptional cases more reasonable. Unfortunately, Go does not seem expressive enough to build the machinery which makes this style of error handling all of reasonable, non-repetitive, and non-confusing.