给出以下Foo
案例类:
scala> case class Foo(x: Int)
defined class Foo
我在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]
最后,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)
^
然而,当我试图在for-comprehension中将bind
链接在一起时,我遇到了上述问题。
正如我所看到的,问题是正确的返回类型是IO[\/[String, Int]]
。但是,我不知道如何验证Foo
和在IO
monad中处理它是否具有for-comprehension。
答案 0 :(得分:5)
你不能将这样的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
}
现在我们可以写下以下内容:
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
这就是 在\/
- 理解中1>}和IO
一起使用的方式。不过,这对你的情况来说肯定是过分杀戮。我可能会写这样的东西:
for
这使用def f(x: Foo): IO[String \/ Foo] =
validateFoo(x).bitraverse(_.point[IO], processFoo)
的{{1}}实例将函数映射到析取两边的Bitraverse
,然后将整个内容转换为内部。
答案 1 :(得分:-2)
您的validateFoo定义中似乎有错误。我想你想要
scala> def validateFoo(foo: Foo): \/[String, Foo] =
(if(foo.x > 0) foo.success[Foo]
else ("invalid foo").failure[String]).disjunction
你在成功和失败案件中混淆了String和Foo。