Типы экзистенциальной сферы в 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] })
. Не уверен, что все это менее запутанно, но именно так я убедил себя, что компилятор знает, что он здесь делает.