Контравариантность и вал - PullRequest
       13

Контравариантность и вал

2 голосов
/ 24 февраля 2011

Как и почему «val» и «case» влияют на систему типов? (Особенно дисперсия)

Welcome to Scala version 2.8.1.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_22).
Type in expressions to have them evaluated.
Type :help for more information.

scala> class E[-A]
defined class E

scala> class F[-A](val f: E[A] => Unit)
<console>:6: error: contravariant type A occurs in covariant position in type => (E[A]) => Unit of value f
class F[-A](val f: E[A] => Unit)
                       ^  
scala> case class C[-A](f: E[A] => Unit)
<console>:6: error: contravariant type A occurs in covariant position in type => (E[A]) => Unit of value f
   case class C[-A](f: E[A] => Unit)

scala> class F[-A](f: E[A] => Unit)    
defined class F

Ответы [ 4 ]

4 голосов
/ 24 февраля 2011

Учитывайте следующее:

trait Equal[-A] { def eq(a1: A, a2: A): Boolean }
val e = new Equal[Option[Int]] { 
    def eq(a1: Option[Int], a2: Option[Int]) = a1 forall (x => a2 forall (x ==)) 
}

// Because Equal is contra-variant, Equal[AnyRef] is a subtype of Equal[String]
// Because T => R is contra-variant in T, Equal[AnyRef] => Unit is a supertype
// of Equal[String] => Unit
// So the follow assignment is valid
val f: Equal[AnyRef] => Unit = (e1: Equal[String]) => println(e1.eq("abc", "def"))


// f(e) doesn't compile because of contra-variance
// as Equal[Option[Int]] is not a subtype of Equal[AnyRef]

// Now let's tell Scala we know what we are doing
class F[-A](val f: Equal[A @uncheckedVariance] => Unit)

// And then let's prove we are not:
// Because F is contra-variant, F[Option[Int]] is a subtype of F[AnyRef]
val g: F[Option[Int]] = new F(f)

// And since g.f is Equal[Option[Int]] => Unit, we can pass e to it.
g.f(e) // compiles, throws exception

Если f не виден снаружи F, эта проблема не может возникнуть.

2 голосов
/ 24 февраля 2011

Вы спрашиваете, что такое дисперсия?Если вы знаете, что такое дисперсия, это говорит само за себя.Пример без «val» или «case» не имеет внешне видимых членов, включающих A, поэтому он не может вызвать ошибку отклонения.

1 голос
/ 24 февраля 2011

'val' означает, что поле является видимым снаружи. Рассмотрим:

val f: E[Any] => Unit = { ... }
val broken: F[Int] = new F[Any](f) // allowed by -A annotation
val f2: E[Int] => Unit = broken.f // must work (types match)
val f3: E[Int] => Unit = f // type error

По сути, нам удалось небезопасно разыграть f, не действуя явно. Это работает только в том случае, если f является видимым, т. Е. Если вы определяете его как val или используете класс case.

0 голосов
/ 24 февраля 2011

Вот противоположный «выходной канал», который просто печатает на консоль:

class OutputChannel[-T] {   
  def write(t:T) = println(t); 
}

Вот он в действии:

val out:OutputChannel[Any] = new OutputChannel[Any]
out.write(5)

Ничего интересного пока нет.Крутая вещь в контрасте - теперь вы можете безопасно назначить этот выходной канал тому, который принимает любой подкласс T:

val out2:OutputChannel[String] = out
out2.write("five")
out2.write(55) //wont compile

Теперь, представьте, если мы добавили отслеживание истории в выходной канал - чтобы вернутьСписок вещей, которые были отправлены до сих пор.

//!!! as you've seen code like this won't compile w/ contravariant types!!!!
class OutputChannel[-T] {   
  var history:List[T] = Nil
  def write(t:T) = { 
    history = history :+ t;  
    println(t); 
  } 
}

Если вышеприведенное скомпилируется, у пользователя выходного канала на основе строк возникнет проблема:

//history(0) is an Int - runtime exception (if scala allowed it to compile)
val firstStringOutputted:String = out2.history(0) 

Поскольку контравариантность допускает это «сужение» типов (т.е. изЛюбое для String здесь), система типов не может отображать значения типа T, такие как это поле «history», которое я сделал, или поле «f», которое вы имели.

Другими известными «противоположностями» являются Function и Comparators:

val strHashCode:String => Int = { s:Any => s.hashCode }  //function which works with any object
val strComp:Comparator<String> = new HashCodeComparator()   //comparator object which works with any object
...