Reputation: 7527
If I have multiple operations that return a Validation[E, _]
of something with a fixed error type I can use them in a for-comprehension. For example:
val things: Validation[E, (Int, Double)] = for {
i <- getValidationOfInt
d <- getValidationOfDouble
} yield (i, d)
What about if the error types are different? Suppose I read from HTTP and want to convert the string response into an Int
.
import scalaz._; import Scalaz._
object ValidationMixing {
class HttpError
def getFromHttp: Validation[HttpError, String] = ???
def parseInt(json: String): Validation[Throwable, Int] =
Validation.fromTryCatchNonFatal(Integer.parseInt(json))
val intParsedFromHttp: Validation[Any, Int] = for {
s <- getFromHttp
i <- parseInt(s)
} yield i
}
This compiles, but only because the error type of the Validation is Any
, being a supertype of Throwable
and HttpError
. This isn't very helpful.
I can think of various ways of representing such a combined error type that are more useful than Any
(e.g. Validation[Error1 \/ Error2, Result]
to store either, Validation[String, Result]
translating to an error message, etc) but they all have drawbacks.
Is there an idiomatic way to do this?
Upvotes: 3
Views: 392
Reputation: 15783
Since nobody has had a better idea I'll leave my answer for future reference.
As said in the comment the best way is to create a error hierarchy:
trait GenericError { /* some commond fields */}
case class MyNumericError(/* fields */)
and then use leftMap
on a validation to generate appropriate errors:
Validation.fromTryCatchNonFatal(...).leftMap(t => MyNumericError(...))
This approach has two advantages
Validation[GenericError, T]
so not having different types on the left part of that validationUpvotes: 1