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 \/ AandIO[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
EitherTto get anIOaction:That's how you could use
\/andIOtogether in afor-comprehension. It's pretty definitely overkill in your case, though. I'd probably write something like this:This uses the
Bitraverseinstance for\/to map a function intoIOover both sides of the disjunction and then turn the whole thing inside out.