使用\ /和IO的For-Understarehension示例

Kev*_*ith 1 scala scalaz

给出以下Foo案例类:

scala> case class Foo(x: Int)
defined class Foo
Run Code Online (Sandbox Code Playgroud)

Foo在构建它之前检查它是否有效validateFoo:

scala> def validateFoo(foo: Foo): \/[String, Foo] = 
              (if(foo.x > 0) foo.success[String] 
              else ("invalid foo").failure[Foo]).disjunction
validateFoo: (foo: Foo)scalaz.\/[String,Foo]
Run Code Online (Sandbox Code Playgroud)

最后,f创建一个Foo,然后尝试执行一个IO操作(例如:保存Foo到数据库).

scala> def f(x: Foo): IO[\/[String,Int]] = for {
     |    foo <- validateFoo(x)
     |    ioFoo <- IO { foo }
     | } yield ioFoo
<console>:19: error: type mismatch;
 found   : scalaz.effect.IO[Foo]
 required: scalaz.\/[?,?]
          ioFoo <- IO { foo }
                ^
<console>:18: error: type mismatch;
 found   : scalaz.\/[String,Nothing]
 required: scalaz.effect.IO[scalaz.\/[String,Int]]
          foo <- validateFoo(x)
              ^
Run Code Online (Sandbox Code Playgroud)

但是,当我试图bind在for-comprehension中将它们联系在一起时,我遇到了上述问题.

正如我所看到的,问题在于正确的返回类型是IO[\/[String, Int]].但是,我不知道如何在monad中验证Foo 处理它以获得IO理解.

Tra*_*own 5

你无法将这样的组合String \/ AIO[A]计算结合起来- 如果你贬低了你的for理解,你就会发现这些类型无法解决.

如果你真的想,你可以使用monad变换器来组成两个monad.假设我们有以下设置(例如,它本质上是您的代码,但清理了一下,并使用表示数据库操作的新方法):

import scalaz._, Scalaz._, effect._

case class Foo(x: Int)

def validateFoo(foo: Foo): String \/ Foo =
  (foo.x > 0) either foo or "invalid foo"

def processFoo(foo: Foo): IO[Foo] = IO {
  println(foo)
  foo
}
Run Code Online (Sandbox Code Playgroud)

现在我们可以写下面的内容:

type IOStringOr[A] = EitherT[IO, String, A]

def f(x: Foo): IOStringOr[Foo] = for {
  foo <- EitherT(validateFoo(x).point[IO])
  ioFoo <- processFoo(foo).liftIO[IOStringOr]
} yield ioFoo
Run Code Online (Sandbox Code Playgroud)

现在您可以运行EitherT以获取IO操作:

val ioAction: IO[String \/ Foo] = f(Foo(10)).run
Run Code Online (Sandbox Code Playgroud)

这就是你可以\/IO在共同for-comprehension.不过,这对你的情况来说肯定是过分杀戮.我可能会写这样的东西:

def f(x: Foo): IO[String \/ Foo] =
  validateFoo(x).bitraverse(_.point[IO], processFoo)
Run Code Online (Sandbox Code Playgroud)

这使用Bitraverse实例\/将函数映射到IO析取的两侧,然后将整个内部转出.