Can I use monad transformers to simplify this composition?

functional-programming, kleisli, monad-transformers, scala, scalaz

Solution

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.

Problem

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)?

Original source