使用 \/ 和 IO 的理解示例

For-Comprehension Example with \/ and IO

给定以下 Foo 案例 class:

scala> case class Foo(x: Int)
defined class Foo

我在 validateFoo:

中构造它之前检查它是否有效 Foo
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]

最后,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)
              ^

但是,我 运行 在尝试将 bind 链接在一起进行理解时遇到了上述问题。

在我看来,问题在于正确的 return 类型是 IO[\/[String, Int]]。但是,我不知道如何验证 Foo IO monad 中用 for-comprehension 处理它。

您的 validateFoo 定义似乎有误。我想你想要

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

您混淆了成功和失败案例中的 String 和 Foo。

您不能像这样组合 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
}

现在我们可以这样写了:

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

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

val ioAction: IO[String \/ Foo] = f(Foo(10)).run

这就是您可以for理解中一起使用\/IO的方式。不过,在您的情况下,这绝对是矫枉过正。我可能会这样写:

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

这使用 \/Bitraverse 实例将一个函数映射到析取两边的 IO 中,然后将整个东西翻转过来。