2011-09-02 1 views
14

「の」の型「製品」を選択しますん:なぜScalaは、私はオプションと値の定義を理解するために作成した場合、予想通り、それが動作のいずれかを含む式と値の定義

scala> for (a <- Some(4); b <- Some(5); val p = a * b) yield p 
res0: Option[Int] = Some(20) 

が同じことを行いますどちらかとの事は私は価値の定義を持っていない場合は動作します:

scala> for (a <- Right(4).right; b <- Right(5).right) yield a * b 
res1: Either[Nothing,Int] = Right(20) 

しかし、私は価値の定義を使用した場合、Scalaはのための理解のために間違ったコンテナ型を推論するようだ:

scala> for (a <- Right(4).right; b <- Right(5).right; val p = a * b) yield p 
<console>:8: error: value map is not a member of Product with Serializable with Either[Nothing,(Int, Int)] 
for (a <- Right(4).right; b <- Right(5).right; val p = a * b) yield p 
          ^

どうしてですか?この振る舞いを回避するにはどんな方法がありますか?

答えて

19

問題はあなたが単純

を書く場合val p = a*b から来ている(< - 右(4).RIGHT; < bは - 右(5).RIGHT)* bを得

それはコンパイルすると正しい結果が得られます。

あなたの問題は、ルーチンは、マップやflatMapはジェネリッククラスM[A](A => B) => M[B](A => M[B]) => M[B]で定義されている、すなわちのために、通常の署名を持っていない2つの原因まず

Either投影 mapflatMapを持っています。 M[A]ルーチンは Either[A,B].RightProjectionで定義されていますが、結果と引数には Either[A,B]があり、予測はありません。

次に、理解のための方法val p = a*bが翻訳されます。スカラリファレンス、6.19 P 90:

ジェネレータP < - E値が続く定義P '= E'はXと X 'は、新鮮な名前である値のペアの次の発生に翻訳 、あります:

(p,p′) <- for([email protected]<-e) yield {val x′@p′ = e′; (x,x′)} 

はのはa <-を落とし、コード少しだけを単純化してみましょう。また、bpは、pppにリライトルールに近い名前に変更され、p'の場合はppとなりました。 aがためにスコープ にすることになった(p < - 右(5).RIGHT; valのPPは*のP =)ルール以下の降伏頁

を、私たちは発電機+定義を交換する必要があります。その周りのものは、for()yield ppです。変更はありません。

for((p, pp) <- for([email protected] <- Right(5).right) yield{val [email protected] = a*p; (x,xx)}) yield pp 

ここでは簡単なマップ

for((p, pp) <- Right(5).right.map{case [email protected] => val [email protected] = a*p; (x,xx)}) yield pp 

に書き換えるためのインナーが問題です。 Right(5).right.map(...)のタイプはEither[Nothing, (Int,Int)]で、Either.RightProjection[Nothing, (Int,Int)]ではありません。それは外側のために働かない(mapにも変換する。Eitherにはmapメソッドがありません。投影のみで定義されています。

エラーメッセージをよく見ると、ProductSerializableが記載されていても、Either[Nothing, (Int, Int)]であり、マップが定義されていないと表示されています。ペア(Int, Int)は、書き換えルールから直接得られます。

適切な署名を守るためには、理解のための理解が必要です。 Either投影(これも利点があります)のトリックで、この問題が発生します。

+1

ああ、それは理にかなっています。あなたは私がすぐにきれいにするだろう最初(私は素数、微妙なタイプのエラーなど)で私をトリップしたいくつかのタイプミスがあります。 – srparish

+0

上記の興味深い結果は、私が一貫性のあるモナドの型を持っていれば、うまくいくということです。だから私は正しい投射だけを気にすると、暗黙のdef RightProjection [A、B](v:[A、B]のどちらか):Either.RightProjection [A、B] = v.right – srparish

+1

もう一度、徹底的な執筆に感謝します!私は参照を読んで、何が起こっていたのかをまだ分かっていなかった。明らかにこの単純な例では、a * bを歩留まりに移動できますが、それが別のEitherを返す関数に渡す必要がある複雑な計算に置き換えられた場合(これを数回繰り返します)、中間を使用できる値の割り当ては、複数のレベルの連鎖/収束ステートメントを持つ必要がなくなり、よりクリーンになります。 – srparish

関連する問題