2013-07-02 8 views
7

私はScalaz 7のEitherTを使ってStateと\ /をブレンドするfor-comprehensionsを構築しています。ここまでは順調ですね;私は基本的に何かを得る:EitherT内でタプルを返す方法

State[MyStateType, MyLeftType \/ MyRightType] 

をし、それは私がのために、内包<の左側にある素敵な変数を持って構築することができます - 。

しかし、私は状態アクションからタプルを返す方法を理解できません。単一の結果はうまくいきます - 下のコードでは、 "価値の理解"はまさに私が起こりたいものです。

しかし、タプルを返すときに物事が崩れます。 「ヴァルotherComprehensionは、」私はそれがモノイドする/ \の左側を期待して、私はその理由を理解していないように見えます

(a, b) <- comprehension 

やらせません。私は何が欠けていますか?

(Scalaz 7 2.0.0-SNAPSHOT、スカラ座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と呼ばれる)ケースクラスであるので、私はゼロを提供していますが、引数のいずれかがゼロである場合にのみ機能します追加することができます

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

私はこれは確信していません良いアイデアだが、それは問題を解決している。

+1

途中で何か不思議なことが起こっています。2.10.1+デューキュアでは、ここでは、[この質問](http://stackoverflow.com/q/17424763/334519)を参照してください。同じ問題。 –

+2

そして、明らかに、これは、 'EitherT'(または' \/')をフィルタリングするために左側のモノイドインスタンスが必要で、何らかの理由で2.10.2がこの' for'-comprehensionにフィルタ演算を張っているために起こります。 –

答えて

4

私は上記のコメントで注意として、問題はあなたの第二for -comprehensionの脱糖バージョンが含まれていることですフィルタ処理は2.10.2(と2.10.1ではなく2.10.0ではなく)、左側の型に対してモノイドインスタンスなしでEitherT(あるいは普通の古い\/)をフィルタリングすることはできません。

それはモノイドは、次の例では必要である理由は非常に簡単です:

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

yは何? String \/ Intのような何らかの「空」でなければならないことは明らかですが、\/は右に偏っているため、その側の値にすることはできません。だから我々は、左側のためにゼロを必要とし、Stringのためのモノイドのインスタンスは、この-それが提供するだけで空の文字列です:

assert(y == "".left) 

this answerによると、タプルのパターンについてmy related questionからfor -comprehensionsで、あなたがしている行動を2.10.2の内容が正しいと思われます。withFilterへの完全に不必要な呼び出しはここにあります。

あなたはペトル・スキー - パドラックの答えで回避策を使用することができますが、それはまた、以下の無糖版もかなり明確かつ簡潔であることは注目に値します:

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

これは、私は単純に、多かれ少なかれ期待されているものfor - とにかく(と私はnot the only oneです)desugarへの理解。

2

原因を知らず、私は可能な回避策が見つかりました:それはとても素敵ではないですが、仕事をしていません

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) 

を。

は(私は本当にあなたが問題の自己完結型、実施例を作ったことを感謝しています。)

+1

x = p._1の代わりに。 y = p._2;あなたはちょうど(x、y)= pを行うことができます。あなたが別の行でそれをする必要があることを奇妙に思う(@TravisBrownからのリンクとコメントには有用な情報がある)。 –

+0

@JamesMoore真正です。 –

関連する問題