I'm using Scalaz 7's EitherT to construct for-comprehensions that blend State and \/. So far so good; I get something that's basically:
State[MyStateType, MyLeftType \/ MyRightType]
and that allows me to build for-comprehensions that have nice variables on the left side of the <-.
But I can't figure out how to return tuples from a state action. Single results are just fine - in the code below, "val comprehension" is exactly what I want to happen.
But things fall apart when I want to return a tuple; "val otherComprehension" won't let me do
(a, b) <- comprehension
It looks like it expects the left side of the \/ to be a Monoid and I don't understand why. What am I missing?
(Scalaz 7 2.0.0-SNAPSHOT, Scala 2.10.2)
object StateProblem {
case class MyStateType
case class MyRightType
case class MyLeftType
type StateWithFixedStateType[+A] = State[MyStateType, A]
type EitherTWithFailureType[F[+_], A] = EitherT[F, MyLeftType, A]
type CombinedStateAndFailure[A] = EitherTWithFailureType[StateWithFixedStateType, A]
def doSomething: CombinedStateAndFailure[MyRightType] = {
val x = State[MyStateType, MyLeftType \/ MyRightType] {
case s => (s, MyRightType().right)
}
EitherT[StateWithFixedStateType, MyLeftType, MyRightType](x)
}
val comprehension = for {
a <- doSomething
b <- doSomething
} yield (a, b)
val otherComprehension = for {
// this gets a compile error:
// could not find implicit value for parameter M: scalaz.Monoid[com.seattleglassware.StateProblem.MyLeftType]
(x, y) <- comprehension
z <- doSomething
} yield (x, y, z)
}
Edit: I've added evidence that MyLeftType is a monad, even though it's not. In my real code, MyLeftType is a case class (called EarlyReturn), so I can provide a zero, but append only works if one of the arguments is a zero:
implicit val partialMonoidForEarlyReturn = new Monoid[EarlyReturn] {
case object NoOp extends EarlyReturn
def zero = NoOp
def append(a: EarlyReturn, b: => EarlyReturn) =
(a, b) match {
case (NoOp, b) => b
case (a, NoOp) => a
case _ => throw new RuntimeException("""this isnt really a Monoid, I just want to use it on the left side of a \/""")
}
}
I'm not convinced this is a good idea, but it's solving the problem.
for
-comprehension here—see this question for a simplified version of the same issue. – LoganiaceousEitherT
(or\/
) requires a monoid instance for the left side, and for some reason 2.10.2 is sticking a filter operation in thisfor
-comprehension. – Loganiaceous