首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在EitherT中返回元组

如何在EitherT中返回元组
EN

Stack Overflow用户
提问于 2013-07-02 13:43:33
回答 2查看 751关注 0票数 8

我正在使用Scalaz7的EitherT来构建混合状态和\/的for-comprehensions。到目前为止还不错;我得到的结果基本上是:

代码语言:javascript
复制
State[MyStateType, MyLeftType \/ MyRightType]

这让我可以构建在<-的左边有很好的变量的for-理解。

但是我不知道如何从状态操作中返回元组。单个结果很好--在下面的代码中,"val comprehension“正是我想要发生的事情。

但是当我想要返回一个元组时,事情就变得一团糟;"val otherComprehension“不让我这样做

代码语言:javascript
复制
(a, b) <- comprehension

它看起来像是期望\/的左侧是Monoid,我不明白为什么。我遗漏了什么?

(Scalaz 7 2.0.0-快照,Scala 2.10.2)

代码语言:javascript
复制
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才起作用:

代码语言:javascript
复制
  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 \/""")
      }
  }

我不相信这是一个好主意,但它解决了问题。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-07-03 08:58:25

正如我在上面的评论中指出的,问题是你的第二个for-comprehension的去糖化版本涉及到2.10.2 (和2.10.1,但不是2.10.0)中的过滤操作,如果没有左侧类型的monoid实例,就不可能过滤EitherT (或普通的旧\/)。

在下面的示例中,很容易看出为什么monoid是必需的:

代码语言:javascript
复制
val x: String \/ Int = 1.right
val y: String \/ Int = x.filter(_ < 0)

什么是y?很明显,它必须是某种“空的”String \/ Int,而且由于\/是右偏的,我们知道它不能是那一边的值。所以我们需要一个0作为左边的值,String的monoid实例提供了这个--它只是一个空字符串:

代码语言:javascript
复制
assert(y == "".left)

根据this answer to my related question关于for-comprehensions中的元组模式的说法,你在2.10.2中看到的行为是正确的,而且是有意的--对withFilter的明显完全不必要的调用将继续存在。

你可以使用Petr Pudlák的答案中的变通方法,但也值得注意的是,下面的无糖版本也非常清晰和简洁:

代码语言:javascript
复制
val notAnotherComprehension = comprehension.flatMap {
  case (x, y) => doSomething.map((x, y, _))
}

这或多或少是我天真地期望for-comprehension去掉垃圾的原因(我是not the only one)。

票数 5
EN

Stack Overflow用户

发布于 2013-07-02 16:52:58

在不知道原因的情况下,我找到了一种可能的解决方法:

代码语言:javascript
复制
for {
  //(x, y) <- comprehension
  p <- comprehension

  z <- doSomething
} yield (p._1, p._2, z)

或者稍微好一点

代码语言:javascript
复制
for {
  //(x, y) <- comprehension
  p <- comprehension
  (x, y) = p

  z <- doSomething
} yield (x, y, z)

这不是很好,但工作很好。

(我真的很感谢您为这个问题做了一个自包含的工作示例。)

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/17418165

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档