Типы экзистенциальной сферы в Scala

немного запутанно об экзистенциальных типах.

это работает для меня:

def valueOf(c: Class[_], name: String) {
  type C = Class[T] forSome {type T <: Enum[T]}
  Enum.valueOf(c.asInstanceOf[C], name)
} 

но это не:

def valueOf(c: Class[_], name: String) {
  type T = T forSome {type T <: Enum[T]}
  Enum.valueOf(c.asInstanceOf[Class[T]], name)
}

на мой взгляд, оба выражения эквивалентны:

Enum.valueOf(z.asInstanceOf[Class[T] forSome {type T <: Enum[T]}], name)

но Скала говорит, что это только у меня в голове:

inferred type arguments [T] do not conform to method valueOf's type parameter bounds [T <: Enum[T]]
         Enum.valueOf(c.asInstanceOf[Class[T]], name)
              ^

1 ответов


рассмотрим разницу между следующими двумя выражениями:

Enum.valueOf(x.asInstanceOf[Class[X] forSome { type X <: Enum[X] }], name)

и:

Enum.valueOf(x.asInstanceOf[Class[X forSome { type X <: Enum[X] }]], name)

теперь подумайте о том, как параметр типа T of valueOf будет определен в каждом из этих случаев. В первом случае, у нас есть X что мы знаем, это подтип Enum[X] и все готово. Во втором случае, с другой стороны, T должен быть X forSome { type X <: Enum[X] }, и принципиально этот тип не является подтипом Enum[X forSome { type X <: Enum[X] }], поэтому мы не удовлетворили ограничение на T.

проблема в том, что ваш второй пример эквивалентен последнему.


как сноска, это будет работать просто отлично, если Enum были ковариантными по параметру типа. Возьмем следующий упрощенный пример:

trait Foo[A]
trait Bar[A]

def foo[A <: Bar[A]](f: Foo[A]) = f

def x: Foo[X] forSome { type X <: Bar[X] } = ???
def y: Foo[Y forSome { type Y <: Bar[Y] }] = ???

теперь foo(x) компилируется, но foo(y) не будет, как и в вашем коде. Но перемены!--17--> немного:

trait Foo[A]
trait Bar[+A]

def foo[A <: Bar[A]](f: Foo[A]) = f

def x: Foo[X] forSome { type X <: Bar[X] } = ???
def y: Foo[Y forSome { type Y <: Bar[Y] }] = ???

теперь они оба будут компилироваться. Я думаю, что это как-то связано по той причине, что у нас есть такие сильные интуиции о том, что ваши два примера эквивалентны.


в качестве еще одной сноски (в ответ на gzmo ' s комментарий ниже), учесть следующее:

scala> trait Foo[A <: Foo[A]]
defined trait Foo

scala> class MyFoo extends Foo[MyFoo]
defined class MyFoo

scala> val myFoo = new MyFoo
myFoo: MyFoo = MyFoo@3ee536d

scala> myFoo: (X forSome { type X <: Foo[X] })
res0: X forSome { type X <: Foo[X] } = MyFoo@3ee536d

scala> myFoo: Foo[MyFoo]
res1: Foo[MyFoo] = MyFoo@3ee536d
давайте, что X forSome { type X <: Foo[X] } были подтипом Foo[X forSome { type X <: Foo[X] }] (игнорируя на мгновение тот факт, что последний даже не является допустимым типом). Тогда мы смогли бы написать следующее:--26-->
myFoo: Foo[X forSome { type X <: Foo[X] }]

но Foo инвариантно, поэтому, если у нас есть что-то, что является экземпляром обоих Foo[A] и Foo[B], тогда это должно быть так, что A =:= B. Но это определенно не тот случай, когда MyFoo =:= (X forSome { type X <: Foo[X] }). Не уверен, что все это менее запутанно, но именно так я убедил себя, что компилятор знает, что он здесь делает.