There are tradeoffs:
scalaz.Validation
is able to accumulate errors of typeE
given aSemigroup[E]
instance. It's intended for use as anApplicative
, like:(fragileFoo |@| fragileBar) { case (foo, bar) => doSomething(foo, bar) }
It does have
map
andflatMap
methods, biased towards theSuccess
side, so you can use it conveniently in afor
-comprehension. However, there is noMonad
instance defined for it, so it can't be used in any higher-order stuff (for example, you can't use it with monad transformers). This shortcoming doesn't seem like it would be a problem for you, though.scalaz.\/
, which you didn't mention, does form aMonad
(again, biased toward theRight
side). But when used as anApplicative
, it doesn't accumulate failures asValidation
does.util.Try
is similar toscalaz.\/
, specialized toThrowable
. Although it again lacks accumulation of errors, it does have the notion of error recovery. However, for your "builder pattern" use case, it seems like this might not be terribly useful.Finally,
util.Either
isn't worth considering, compared to the other three options: because it's not biased toward one side or the other, you have to explicitly and consistently ask for theleft
orright
projection every time you want to do something monadic.
My best guess is that for your situation, scalaz.Validation
is the most appropriate choice.