Uso l'Etilit di Scalaz 7 per costruire le incomprensioni che fondono State e \ /. Fin qui tutto bene; Ottengo qualcosa che è fondamentalmente:Come restituire una tupla in un EitherT
State[MyStateType, MyLeftType \/ MyRightType]
e che mi permette di costruire per-comprensioni che hanno belle variabili sul lato sinistro della < -.
Ma non riesco a capire come restituire le tuple da un'azione di stato. I singoli risultati vanno bene - nel codice qui sotto, "val comprehension" è esattamente ciò che voglio accadere.
Ma le cose vanno a pezzi quando voglio restituire una tupla; "Val otherComprehension" non mi lascia fare
(a, b) <- comprehension
Sembra che si aspetta che il lato sinistro del \/per essere un Monoide e non capisco perché. Cosa mi manca?
(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: ho la prova che MyLeftType è una monade, anche se non è aggiunto. Nel mio codice reale, MyLeftType è una classe caso (chiamato EarlyReturn), così posso fornire uno zero, ma accodare funziona solo se uno degli argomenti è uno 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 \/""")
}
}
Io non sono convinto che questo sia un buona idea, ma sta risolvendo il problema.
C'è qualcosa di strano nel modo in cui 2.10.1+ desugars la 'for'-comprensione qui-vedi [questa domanda] (http://stackoverflow.com/q/17424763/334519) per una versione semplificata del stesso problema. –
E per essere chiari, questo sta accadendo perché il filtro 'EitherT' (o' \/') richiede un'istanza monoid per il lato sinistro, e per qualche ragione 2.10.2 sta attaccando un'operazione di filtro in questo' for'-comprehension. –