Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way to handle object's fields validation => Either / Try (scala 2.10) / ValidationNEL (scalaz)

Let's assume an object constructed using a builder pattern.

This builder pattern would contain a build method focusing on fields validation and then on conversion to the targeted type.

This validation could be implemented using:

  • Either[FailureObject, TargetObject] type
  • Try[TargetObject] (new feature from Scala 2.10)
  • Validation[FailureObject, TargetObject] or ValidationNEL[FailureObject, TargetObject] from scalaz library

I read that one of the main advantages of Validation over Either type is that Validation can accumulate failures "out of the box".

But what about the "new" Try way? I noticed that Try has "monadic" methods out of the box also, like map, flatMap etc... what was really missing with Either type without help of Projection.

Thus, I'd imagine each field validation method returning a Try[FieldType] and more precisely, in case of any failure, a Try[SpecificFieldExceptionType]; this nested one containing a String message field and a rootCause field that could be accumulated throughout the build method.

Using Scala 2.10, could or should Try practice replace scalaz validation library for simple validation like builder pattern involves?

**EDIT ****

By reading Try source code, it sounds that Try can't accumulate several exceptions and thus is oriented fail-fast. Even Try.flatMapreturns the potentential previous failure and so doesn't have the notion of accumulation:

def flatMap[U](f: T => Try[U]): Try[U] = this.asInstanceOf[Try[U]]

On the contrary of ValidationNEL that handles accumulation feature.

Any confirmation?

like image 935
Mik378 Avatar asked Feb 27 '13 18:02

Mik378


1 Answers

There are tradeoffs:

  • scalaz.Validation is able to accumulate errors of type E given a Semigroup[E] instance. It's intended for use as an Applicative, like:

    (fragileFoo |@| fragileBar) { case (foo, bar) => doSomething(foo, bar) }

    It does have map and flatMap methods, biased towards the Success side, so you can use it conveniently in a for-comprehension. However, there is no Monad 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 a Monad (again, biased toward the Right side). But when used as an Applicative, it doesn't accumulate failures as Validation does.

  • util.Try is similar to scalaz.\/, specialized to Throwable. 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 the left or right projection every time you want to do something monadic.

My best guess is that for your situation, scalaz.Validation is the most appropriate choice.

like image 128
mergeconflict Avatar answered Sep 19 '22 23:09

mergeconflict