13

最近、型エイリアスでバリアンス アノテーションを使用できることに気付きました。からの例を次に示しPredefます。

type Function[-A, +B] = Function1[A, B]

そして、どこで使用できるかを考え始めました。明らかに、分散を反対に変更したり、不変型を共変または反変として動作させることはできません。コンパイラは次のようにエラーをスローします

scala> type BrokenFunc[+T, -R] = Function1[T, R]
<console>:7: error: covariant type T occurs in contravariant position in type 
  [+T, -R]T => R of type BrokenFunc

ただし、一部のバリアント型を不変のように動作させることができます (少なくとも、コンパイラはそれについて議論しません)。だから、私はの不変バージョンを作ろうとしましたList

scala> type InvList[T] = List[T]
defined type alias InvList

しかし、この新しい不変List条件は、元の共変バージョンと同じように動作します。

scala> val l: InvList[String] = List("foo")
l: InvList[String] = List(foo)

scala> val anyList: InvList[Any] = l
anyList: InvList[Any] = List(foo)

それで、私は何が欠けていますか?型エイリアスの分散注釈の目的は何ですか? 元の型とは異なる、分散注釈付きの型エイリアスの例を教えてください。

4

2 に答える 2

8

というわけで、はっきりとはわかりませんが、考えられる説明を提供します。

Scala の型エイリアスはかなり「弱い」です。新しい型を完全に作成するのではなく、古い型 (および新しいパス依存型) を新しい方法で作成するだけです。これは、定義した場合

type InvList[T] = List[T]

そして書くInvList[T]、それはあなたが書いたかのようですList[T]。だからこそInvList[Int] <: InvList[Any]、なぜなら、書き直した、これはただのList[Int] <: List[Any]. Scala の型エイリアスがどの程度「弱い」のか、実際には正確にはわかりません...パス依存の型のため、Haskell のエイリアスよりも少し強力ですが、クラス宣言よりは弱いです。他の誰かがさらに説明できるかもしれません。

では、なぜ Scala はバリアンス アノテーションをそこに置くことを許可しているのですか? バリアンス アノテーションを無視して型を書き換えるだけなのでしょうか? タイプメンバー用です。と言っていただけるほどです

trait A { type F[+T] }

+T実装が分散に準拠することを要求するため、許可します

trait B extends A { type F[+T] = List[T] }

だがしかし

trait C extends A { type F[T] = Function[T,T] }

または、Scala Language Spec S4.3のこの文のように。

型コンストラクターの宣言は、t が表す具体的な型に追加の制限を課します。境界 L および U に加えて、型パラメーター句は、型コンストラクターの適合性 (§3.5.2) によって管理されるように、高次の境界と分散を課すことができます。

于 2012-05-10T06:41:04.407 に答える
2

抽象型が (のように) 宣言されているだけの場合Domain、不変性が適用されます。DomainImpl抽象型がより緩和された分散を持つように定義されている場合、これは ( のように)知られていると尊重されます。

trait Domain {
  type InvList[T]

  val xi: InvList[Int]
  private val xa: InvList[Any] = xi // won't compile
}

class DomainImpl extends Domain {
  type InvList[T] = List[T]

  val xi: InvList[Int] = List(1)
  private val xa: InvList[Any] = xi // will compile
}
于 2012-05-10T05:52:00.117 に答える