8

State と \/ をブレンドする for 内包表記を構築するために、Scalaz 7 の EachT を使用しています。ここまでは順調ですね; 私は基本的に何かを得ます:

State[MyStateType, MyLeftType \/ MyRightType]

これにより、<- の左側に素敵な変数を持つ for 内包表記を作成できます。

しかし、状態アクションからタプルを返す方法がわかりません。単一の結果は問題ありません。以下のコードでは、「val 内包表記」がまさに私が望んでいることです。

しかし、タプルを返したいときはうまくいきません。「val otherComprehension」は私をさせません

(a, b) <- comprehension

\/ の左側がモノイドであることを期待しているように見えますが、その理由がわかりません。私は何が欠けていますか?

(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)
}

編集: MyLeftType がモナドであるという証拠を追加しましたが、そうではありません。私の実際のコードでは、MyLeftType はケース クラス (EarlyReturn と呼ばれます) であるため、0 を指定できますが、引数の 1 つが 0 の場合にのみ追加が機能します。

  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 \/""")
      }
  }

これが良いアイデアだとは確信していませんが、問題は解決しています。

4

2 に答える 2

5

原因を知らずに、可能な回避策を見つけました:

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)

それはあまりいいことではありませんが、仕事をします。

(問題の自己完結型の実用的な例を作成してくれたことに本当に感謝しています。)

于 2013-07-02T08:52:57.617 に答える
5

上記のコメントで指摘したように、問題は、for2.10.2 (および 2.10.1 であり、2.10.0 ではない) でのフィルタリング操作が、 2 番目の内包表記の脱糖バージョンに含まれており、フィルタリングEitherT(または単純な) ができないことです。 old \/) 左側の型のモノイド インスタンスなし。

次の例では、モノイドが必要な理由を簡単に確認できます。

val x: String \/ Int = 1.right
val y: String \/ Int = x.filter(_ < 0)

とはy? ある種の「空」でなければならないことは明らかでString \/ Intあり、\/は右バイアスであるため、その側の値にはなり得ないことがわかっています。したがって、左側にゼロが必要であり、のモノイド インスタンスがStringこれを提供します。これは単なる空の文字列です。

assert(y == "".left)

-comprehensions のタプル パターンに関する私の関連する質問に対するこの回答によると、2.10.2 で見られる動作は正しく、意図されたものです。forwithFilter

Petr Pudlák の回答で回避策を使用できますが、次の無糖バージョンも非常に明確で簡潔であることにも注意してください。

val notAnotherComprehension = comprehension.flatMap {
  case (x, y) => doSomething.map((x, y, _))
}

とにかく、これは多かれ少なかれ、-comprehension が desugar することを素朴に期待するものforです (そして、私だけではありません)。

于 2013-07-03T00:58:25.440 に答える