Error, A] 3
4 val result: Result[Int] = 62.point[Result] //> result : Exercises.monadtxns.Result[Int] = OptionT(\/-(Some(62)))
5 val transformed =
6 for { 7 value <- result 8 } yield value + 18 //> transformed : scalaz.OptionT[Exercises.monadtxns.Error,Int] = OptionT(\/-(S 9 //| ome(80)))
现在,运算A只需要一层context了。Result就是通过Monad Transformer产生的新类型。在上面的类型构建里,OptionT就是一个Monad Transformer、Error是固定了Left类型的Either。因为Either有两个类型参数,我们实际上也可以直接用type lambda来表示Result[A]:
type Result[A] = OptionT[({type l[x] = \/[String,x]})#l,A]
不过这样写不但复杂,而且会影响编译器的类型推导(compiler type inference)。
值得注意的是,Monad Transformer 类型的构建是由内向外反向的。比如上面的例子中OptionT是个Monad Transformer,它的类型款式是OptionT[M[_],A]。OptionT实际上是用来构建M[Option[A]],在我们的例子里就是Either[Option[A]]。我们来看看一些常用Monad Transformer的类型款式:
final case class OptionT[F[_], A](run: F[Option[A]]) { ... final case class EitherT[F[_], A, B](run: F[A \/ B]) { ... final case class ListT[F[_], A](run: F[List[A]]){ ... trait IndexedStateT[F[_], -S1, S2, A] { self =>
/** Run and return the final value and state in the context of `F` */ def apply(initial: S1): F[(S2, A)]
可以看到,Monad Transformer 的主要作用就在构成run这个我们称为嵌入值了。F可以是任何普通Monad。在上面的例子就变成了:
OptionT[Either,A](run: Either[Option[A]]),这个Either[Option[A]]就是我们的目标类型。而我们在操作时如在for-comprehension中运算时使用的类型则必须统一为OptionT[Either,A]。
我们如何去构建Monad Transformer类型值呢?我们可以用Applicative[MT].point或者直接用构建器方式如OptionT(...)
//point升格
Applicative[Result].point(62) //> res0: Exercises.monadtxns.Result[Int] = OptionT(\/-(Some(62))) //简写版本
62.point[Result] //> res1: Exercises.monadtxns.Result[Int] = OptionT(\/-(Some(62))) //会产生错误结果
None.point[Result] //> res2: Exercises.monadtxns.Result[None.type] = OptionT(\/-(Some(None)))
"Oh,shit!".left.point[Result] //> res3: Exercises.monadtxns.Result[scalaz.\/[String,Nothing]] = OptionT(\/-(So //| me(-\/(Oh,shit!)))) //用构建器
OptionT((None: Option[Int]).point[Error]) //> res4: scalaz.OptionT[Exercises.monadtxns.Error,Int] = OptionT(\/-(None))
OptionT(none[Int].point[Error]) //> res5: scalaz.OptionT[Exercises.monadtxns.Error,Int] = OptionT(\/-(None))
OptionT("Oh,shit!".left: Error[Option[Int]]) //> res6: scalaz.OptionT[Exercises.monadtxns.Error,Int] = OptionT(-\/(Oh,shit!))
与重新构建另一个类型不同的是,通过Monad Transformer叠加Monad组合形成类型的操作依然使用各组成Monad的操作函数,这些函数运算结果类型任然是对应的Monad类型,所以需要一些升格函数(lifting functions)来统一类型。而重建类型则继承了组成Monad的操作函数,它们的运算结果类型都与新建的这个类型一致。下面我们还是用上面的这个Either+Option例子来示范。我们把Either和Option叠加后按照不同顺序可以产生Either[Option[A]]或者Option[Either[A]]两种结果类型,所以叠加顺序是非常重要的,因为这两种类型代表着截然不同的意义:Either[Option[A]]代表一个运算结果可以是成功right或者失败left,如果运算成功则返回一个结果或空值;而Option[Either[A]]从字面上理解好像是一个运算可以返回一个成功或失败的运算又或者返回空值,应该是没有任何意义的一个类型。前面我们提到过用Monad Transformer叠加Monad是由内向外反方向的:获取Either[Option[A]]就需要用OptionT[Either,A]。而且我们需要把Either和Option升格成OptionT[Either,A],看下面的示范:
1 type Error[A] = \/[String, A] 2 type Result[A] = OptionT[Error, A] 3
4 def getString: Option[String] = "Hello ".some //> getString: => Option[String]
5 def getResult: Error[String] = "how are you!".right 6 //> getResult: => Exercises.monadtxns.Error[String]
7 val prg: Result[String] = for { 8 s1 <- OptionT.optionT(getString.point[Error]) 9 s2 <- "World,&quo