On Mon, Aug 27, 2012 at 10:07 AM, Bruce Eckel wrote:
I don't want to explain to new programmers that they have to use Left and
Right instead of Success and Failure, especially now that (with the
introduction of Try) Success and Failure will be staring them in the face.
That's the kind of embarassing explanation that I've gotten really tired of
Using Either was just an experiment. There's nothing that binds us to
using it. The client programmer must always understand what a function
returns, so why not let go of Either and use something that conveys useful
I've said this twice already. Maybe the third time will be the charm.
*Either is not just for error handling.*
In my code I have used Either in the following ways which have nothing to
do with error handling:
(1) Input data can be floats or doubles;
(2) Entry forms with drop-down options plus text are
Either[ListedItems,String] depending on whether the user chooses one or
types their own
(3) There are two different FFT routines I use; I provide a common
interface with Either holding the non-identical optional settings for the
(4) I use Either when command-line settings can be read from a file to
hold the file name or the command, as I showed before
(5) When recursing directories to collect information, I have
(6) While processing errors, I have Either[Exception,Exception] to
segregate the exceptions I can/cannot handle.
(7) I provide a common interface to static and lazy processing with
Either[X, () => X].
(and probably more that I have overlooked at the moment).
So, Either is useful well beyond its application to error handling.
Getting rid of Either would either cause a lot of repetitive work, or force
people back to AnyRef with no type-level support for what might actually be
there (and probably unsafe casting).
Now, given that you already have Either, because it is generically useful,
do you or do you not want to create a clone that works much like Either but
sounds like it is specifically for errors? DRY suggests no. Use the
generic construct *unless* you have a significant value-add with the clone.
And Try does have a significant value add: it handles exceptions for you,
catching and packaging them up as Failure. It (aside from bugs) catches
every exception, always. So you can do something like
Try(s.toLong.toInt).recover(_ => s.toDouble.toInt).map(myArray)
and either the whole thing will work (yay!) or you'll get an exception
(boo!), and you didn't have to think about three different try-catch
blocks. It can do this *specifically because it only handles exceptions*.
If it handled arbitrary errors, it would again have to fall back to AnyRef
(or Either[Throwable,X], or have three states) and would be clunky and/or
invite a lack of type safety.
- If you want to be bulletproof to exceptions, use Try
- If you want to hold either of two types, use Either
(Hint: error value and correct value is a useful case of needing two
types; in that case the convention is that Right is the correct value.)
Now, the one drawback of Either is that it is not biased, which means that
it is equally inconvenient to work with either Lefts or Rights. Rob
Dickens has a pretty good implementation of a right-biased Either, where
the right side is the favored side (map, flatMap, etc. will work on that
side). We can discuss whether this or something based on it should replace
the existing unbiased Either.
I don't think it's too hard for people to understand that it's usually good
to use a powerful generic construct to help them with their error-handling
tasks instead of using a construct that's specialized for
exception-handling. You do have to present it with a modicum of care, but
you have to do that anyway since more often than not you'll use Option not
Either when something could result in not a value. (You don't care why,
usually. You just want your value if you can have it.)