我正在使用Scalaz7的EitherT来构建混合状态和\/的for-comprehensions。到目前为止还不错;我得到的结果基本上是:
State[MyStateType, MyLeftType \/ MyRightType]这让我可以构建在<-的左边有很好的变量的for-理解。
但是我不知道如何从状态操作中返回元组。单个结果很好--在下面的代码中,"val comprehension“正是我想要发生的事情。
但是当我想要返回一个元组时,事情就变得一团糟;"val otherComprehension“不让我这样做
(a, b) <- comprehension它看起来像是期望\/的左侧是Monoid,我不明白为什么。我遗漏了什么?
(Scalaz 7 2.0.0-快照,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)
}编辑:我添加了MyLeftType是单体的证据,尽管它不是。在我的实际代码中,MyLeftType是一个case类(称为EarlyReturn),所以我可以提供一个零,但仅当其中一个参数为零时,append才起作用:
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 \/""")
}
}我不相信这是一个好主意,但它解决了问题。
发布于 2013-07-03 08:58:25
正如我在上面的评论中指出的,问题是你的第二个for-comprehension的去糖化版本涉及到2.10.2 (和2.10.1,但不是2.10.0)中的过滤操作,如果没有左侧类型的monoid实例,就不可能过滤EitherT (或普通的旧\/)。
在下面的示例中,很容易看出为什么monoid是必需的:
val x: String \/ Int = 1.right
val y: String \/ Int = x.filter(_ < 0)什么是y?很明显,它必须是某种“空的”String \/ Int,而且由于\/是右偏的,我们知道它不能是那一边的值。所以我们需要一个0作为左边的值,String的monoid实例提供了这个--它只是一个空字符串:
assert(y == "".left)根据this answer to my related question关于for-comprehensions中的元组模式的说法,你在2.10.2中看到的行为是正确的,而且是有意的--对withFilter的明显完全不必要的调用将继续存在。
你可以使用Petr Pudlák的答案中的变通方法,但也值得注意的是,下面的无糖版本也非常清晰和简洁:
val notAnotherComprehension = comprehension.flatMap {
case (x, y) => doSomething.map((x, y, _))
}这或多或少是我天真地期望for-comprehension去掉垃圾的原因(我是not the only one)。
发布于 2013-07-02 16:52:58
在不知道原因的情况下,我找到了一种可能的解决方法:
for {
//(x, y) <- comprehension
p <- comprehension
z <- doSomething
} yield (p._1, p._2, z)或者稍微好一点
for {
//(x, y) <- comprehension
p <- comprehension
(x, y) = p
z <- doSomething
} yield (x, y, z)这不是很好,但工作很好。
(我真的很感谢您为这个问题做了一个自包含的工作示例。)
https://stackoverflow.com/questions/17418165
复制相似问题