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
andIO[A]
computations like this—if you desugared yourfor
-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):
Now we can write the following:
Now you can run the
EitherT
to get anIO
action:That's how you could use
\/
andIO
together in afor
-comprehension. It's pretty definitely overkill in your case, though. I'd probably write something like this:This uses the
Bitraverse
instance for\/
to map a function intoIO
over both sides of the disjunction and then turn the whole thing inside out.