1

この質問「Either cruft との取引は何ですか?」 に対する適切な回答に続いて、Either[+A, +B] の場合、joinLeft は次のように定義されます。

joinLeft [A1 >: A, B1 >: B, C] (implicit ev: <:<[A1, Either[C, B1]]):
         Either[C, B1]

@Didier は、「A1 と B1 は技術的に必要ですが、理解する上で重要ではありません...」と述べました。しかし、なぜ私たちがただ持つことができないのかについて興味があります

joinLeft[C](implicit ev: <:<[A, Either[C, B]): Either[C, B]

EDITED
分散の概念を joinLeft に適用しようとしました。まず、joinLeft のソース:

abstract class Either[+A, +B]

def joinLeft[A1 >: A, B1 >: B, C](implicit ev: A1 <:< Either[C, B1]):
    Either[C, B1] = this match {
    case Left(a)  => a
    case Right(b) => Right(b)
}

私は間違っているかもしれませんが、私には 2ed のケースの方が簡単に思えます:
Right(b)を満たす必要があり Either[C, B1] >: Either[C, B]ます。Each は共変であるため、B1 >: B.

しかし、問題があります。なぜEither[C, B1]、単なる ではなく、タイプの joinLeft があるのEither[C, B]でしょうか。制約が少ないからでしょうか。

implicit ev: (<:<[A1, Either[C, B1]])A1A のスーパータイプから A1 のスーパータイプにのみ変換できEither[C, B1]ます。a最初のケースで使用されていると思います。でも、A1は必要ないと思います。joinLeft を次のように定義できますか。

def joinLeft[B1 >: B, C](implicit ev: A <:< Either[C, B1])

? 私の分析はナンセンスかもしれません。お気軽に訂正してください。


EDITEDどちらかのjoinLeftに関する
私の別の質問タイプの制約と具体化への答えは、これに関連しています。


編集済み

これを追跡してくれた@sschaefに感謝します。関連する議論はここにあります:


4

1 に答える 1

3

これは、型パラメーターが共変であるためです ( source )。

abstract class Either[+A, +B]

分散を説明する最も簡単な方法は、リストを使用することです。

abstract class MList[A]
case class Cons[A](head: A, tail: MList[A]) extends MList[A]
object MNil extends MList[Nothing]

そのようなリストをインスタンス化しようとすると、うまくいきません:

scala> Cons(1, MNil)
<console>:12: error: type mismatch;
 found   : MNil.type
 required: MList[Int]
Note: Nothing <: Int (and MNil.type <: MList[Nothing]), but class MList is invariant in type A.
You may wish to define A as +A instead. (SLS 4.5)
              Cons(1, MNil)
                      ^

エラー メッセージが示すように、問題は型Aが不変であることです。これは、型( )Bのサブタイプである型に対して、 と言うのは有効ではないことを意味します。AB <: AList[B] <: List[A]

A共変を宣言することでこれを変更できます。

abstract class MList[+A]
// rest as before

scala> Cons(1, MNil)
res3: Cons[Int] = Cons(1,MNil$@5ee988c6)

共分散とは、型のサブタイプの関係が外側の型に転送されることを意味します。この例では、が Scala のボトム型であるMList[Nothing] <: MList[Int]ためNothing、可能なすべての型のサブタイプであることを意味します。

しかし今、問題があります。MListtype のパラメーターを期待するメソッドを追加することはできませんA:

scala> :paste
// Entering paste mode (ctrl-D to finish)

abstract class MList[+A] {
  def Cons(b: A) = new Cons(b, this)
}
case class Cons[A](head: A, tail: MList[A]) extends MList[A]
object MNil extends MList[Nothing]

// Exiting paste mode, now interpreting.

<console>:11: error: covariant type A occurs in invariant position in type (b: A)Cons[A] of method Cons
         def Cons(b: A) = new Cons(b, this)
             ^
<console>:11: error: covariant type A occurs in contravariant position in type A of value b
         def Cons(b: A) = new Cons(b, this)
                  ^

コンパイラがこのコードを拒否する理由を説明するには、システムをもう少し深く掘り下げる必要があります。List[B] <: List[A]の場合は常に真であると仮定しますB <: A。次に、次のコードがコンパイルされます。

val xs: Array[Any] = Array[Int](18)
xs(0) = "hello"

なぜならInt <: Any、それもまた真実であるからArray[Int] <: Array[Any]です。また、これも真実でString <: Anyあるため、コンパイラはこのコードを問題なく変換できますが、実行時に失敗します。したがって、Scala では、aから an への代入は無効であり、コードは拒否されます。それにもかかわらず、エラーは発生しません。StringArray[Int]Array[Int]Array[Any]List

scala> val xs: List[Any] = List[Int](18)
xs: List[Any] = List(18)

動作が異なる理由は、Listが共変でArrayあるのに対し、そうでない (不変) ためです。しかし、なぜ違いがあるのでしょうか。これは、 の不変の性質のため、Lista の要素をList変更することはできません。しかしArray、要素を変更することはできます。つまり、変数を使用すると、プログラマーは、要素が変更可能かどうか、およびコードで誰も愚かなことをしないように注意する必要があるという情報をコンパイラーに与えることができます。

MList修正が必要な問題に戻ります。不変であるため、共変として安全に宣言できます。実際、そうしないと使えないので、そうする必要がありますMNil。オブジェクトに型パラメーターを与えることはできないため、後で型の問題を回避するために、MList可能な限り低い型で拡張する必要がありNothingます。この問題を解決するには、下限を設定する必要があります。

abstract class MList[+A] {
  def Cons[B >: A](b: B) = new Cons(b, this)
}
case class Cons[A](head: A, tail: MList[A]) extends MList[A]
object MNil extends MList[Nothing]

scala> MNil Cons 2 Cons 1
res10: Cons[Int] = Cons(1,Cons(2,MNil$@2e6ef76f))

B >: Aは下限であり、手段Aは のサブタイプBまたはBのスーパータイプですA。(つまり) があるMList[Int]ときにを取得するには、 と があるため、そのような下限が必要です。MList[Nothing]MNilInt >: NothingMList[Nothing].Cons[Int].Cons[Int]

そのため、そのjoinLeftような下限も必要です。実際、joinLeft2 番目の型のバインドのみが必要であり、最初の型パラメーターで既に反変でB1 >: Bあるため、もう 1 つの型は必要ありません。<:<これは、 on の下限にAは何の効果もないことを意味します (補足: on の下限を削除するプル リクエストを開きましたAが、後方ソース互換性が失われ、回避策が見つからなかったため、変更は拒否されました。 )

于 2013-04-12T18:28:45.527 に答える