suppose I have
type VS[A] = Validation[String, A]
val v: VS[Option[A]]
val f: A => VS[B]
I want to get a result of type VS[Option[B]]
but if v
is a Success(None)
, the result should also be a Success(None)
. Here's an example:
scala> val v: VS[Option[String]] = some("4.5").success
v: VS[Option[String]] = Success(Some(4.5))
scala> val f = (s : String) => (try { s.toInt.success } catch { case x => x.getMessage.fail }): VS[Int]
f: String => VS[Int] = <function1>
Then:
scala> import Validation.Monad._
import Validation.Monad._
scala> (v map2 f map (_.sequence)).join
res4: scalaz.Validation[String,Option[Int]] = Failure(For input string: "4.5")
The success case is:
scala> val v: VS[Option[String]]= some("5").success
v: VS[Option[String]] = Success(Some(5))
scala> (v map2 f map (_.sequence)).join //UGLY composition
res7: scalaz.Validation[String,Option[Int]] = Success(Some(5))
And the empty case is:
scala> val v: VS[Option[String]]= none[String].success
v: VS[Option[String]] = Success(None)
scala> (v map2 f map (_.sequence)).join
res6: scalaz.Validation[String,Option[Int]] = Success(None)
Is there a "nicer" way of doing this (possibly involving kleisli composition or monad transformers)?
The monad transformer OptionT
does exactly what you want here, and its flatMapF
method makes usage a clean one-liner.
I'm going to use Scalaz 7's disjunction type (\/
) instead of Validation
in this example, since the latter isn't a monad in Scalaz 7, but the principle is the same.
import scalaz._, std.option._, syntax.id._, syntax.monad._
type DS[+A] = String \/ A
type ODS[A] = OptionT[DS, A]
def f(s: String) = try s.toInt.right catch { case e => e.getMessage.left }
Now we can write the following:
scala> val v = OptionT(some("4.5").point[DS])
v: scalaz.OptionT[DS,java.lang.String] = OptionT(\/-(Some(4.5)))
scala> (v flatMapF f).run
res0: DS[Option[Int]] = -\/(For input string: "4.5")
Or equivalently:
scala> ("4.5".point[ODS] flatMapF f).run
res1: DS[Option[Int]] = -\/(For input string: "4.5")
Or the success case:
scala> ("4".point[ODS] flatMapF f).run
res2: DS[Option[Int]] = \/-(Some(4))
Or the empty case:
scala> (OptionT(none.point[DS]) flatMapF f).run
res3: DS[Option[Int]] = \/-(None)
As desired.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With