给出以下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理解.
你无法将这样的组合String \/ A和IO[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析取的两侧,然后将整个内部转出.
| 归档时间: |
|
| 查看次数: |
358 次 |
| 最近记录: |