What is the difference between Try and Either?
Either
does not imply success and failure, it is just a container for either an A or a B. It is common to use it to represent successes and failures, the convention being to put the failure on the left side, and the success on the right.
A Try
can be seen as an Either
with the left-side type set to Throwable
. Try[A]
would be equivalent to Either[Throwable, A]
.
Use Try
to clearly identify a potential failure in the computation, the failure being represented by an exception. If you want to represent the failure with a different type (like a String, or a set of case classes extending a sealed trait for example) use Either
.
To narrowly answer your question: "What's the semantic difference":
This probably refers to flatMap and map, which are non-existent in Either and either propagate failure or map the success value in Try. This allows, for instance, chaining like
for {
a <- Try {something}
b <- Try {somethingElse(a)}
c <- Try {theOtherThing(b)}
} yield c
which does just what you'd hope - returns a Try containing either the first exception, or the result.
Try has lots of other useful methods, and of course its companion apply method, that make it very convenient for its intended use - exception handling.
If you really want to be overwhelmed, there are two other classes out there which may be of interest for this kind of application. Scalaz has a class called "\/" (formerly known as Prince), pronounced "Either", which is mostly like Either, but flatMap and map work on the Right value. Similarly, and not, Scalactic has an "Or" which is also similar to Either, but flatMap and map work on the Left value.
I don't recommend Scalaz for beginners.
I covered the relationship between Try
, Either
, and Option
in this answer. The highlights from there regarding the relationship between Try
and Either
are summarized below:
Try[A]
is isomorphic to Either[Throwable, A]
. In other words you can treat a Try
as an Either
with a left type of Throwable
, and you can treat any Either
that has a left type of Throwable
as a Try
. It is conventional to use Left
for failures and Right
for successes.
Of course, you can also use Either
more broadly, not only in situations with missing or exceptional values. There are other situations where Either
can help express the semantics of a simple union type (where value is one of two types).
Semantically, you might use Try
to indicate that the operation might fail. You might similarly use Either
in such a situation, especially if your "error" type is something other than Throwable
(e.g. Either[ErrorType, SuccessType]
). And then you might also use Either
when you are operating over a union type (e.g. Either[PossibleType1, PossibleType2]
).
Since Scala 2.12, the standard library does include the conversions from Either
to Try
or from Try
to Either
. For earlier versions, it is pretty simple to enrich Try
, and Either
as needed:
object TryEitherConversions {
implicit class EitherToTry[L <: Throwable, R](val e: Either[L, R]) extends AnyVal {
def toTry: Try[R] = e.fold(Failure(_), Success(_))
}
implicit class TryToEither[T](val t: Try[T]) extends AnyVal {
def toEither: Either[Throwable, T] =
t.map(Right(_)).recover(Left(_)).get
}
}
This would allow you to do:
import TryEitherConversions._
//Try to Either
Try(1).toEither //Either[Throwable, Int] = Right(1)
Try("foo".toInt).toEither //Either[Throwable, Int] = Left(java.lang.NumberFormatException)
//Either to Try
Right[Throwable, Int](1).toTry //Success(1)
Left[Throwable, Int](new Exception).toTry //Failure(java.lang.Exception)