Given the following Foo
case class:
scala> case class Foo(x: Int)
defined class Foo
I check if it's a valid Foo
before constructing it inside of 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]
Lastly, f
creates a Foo
, and then tries to perform an IO
action (example: save Foo
to database).
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)
^
However, I ran into the above issue when trying to chain the bind
's together in the for-comprehension.
The problem, as I see it, is that the proper return type is IO[\\/[String, Int]]
. However, I don't know how to validate Foo
and handle it in the IO
monad with a for-comprehension.
You can't combine String \\/ A
and IO[A]
computations like this—if you desugared your for
-comprehension you'd see that the types just don't work out.
If you really wanted to, you could use a monad transformer to compose the two monads. Suppose we have the following setup, for example (which is essentially your code, but cleaned up a bit, and with a new method representing the database operation):
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
}
Now we can write the following:
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
Now you can run the EitherT
to get an IO
action:
val ioAction: IO[String \/ Foo] = f(Foo(10)).run
That's how you could use \\/
and IO
together in a for
-comprehension. It's pretty definitely overkill in your case, though. I'd probably write something like this:
def f(x: Foo): IO[String \/ Foo] =
validateFoo(x).bitraverse(_.point[IO], processFoo)
This uses the Bitraverse
instance for \\/
to map a function into IO
over both sides of the disjunction and then turn the whole thing inside out.
It looks like you have an error in your definition of validateFoo. I think you want
scala> def validateFoo(foo: Foo): \/[String, Foo] =
(if(foo.x > 0) foo.success[Foo]
else ("invalid foo").failure[String]).disjunction
You've mixed up the String and Foo in the success and failure cases.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.