Рассмотрим разницу между следующими двумя выражениями:
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
из 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)
не будет, так же, как в вашем коде. Но сменить Bar
немного:
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 comment below), необходимо учитывать следующее:
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 = [email protected]
scala> myFoo: (X forSome { type X <: Foo[X] })
res0: X forSome { type X <: Foo[X] } = [email protected]
scala> myFoo: Foo[MyFoo]
res1: Foo[MyFoo] = [email protected]
Давайте предположим, что X forSome { type X <: Foo[X] }
были подтипом Foo[X forSome { type X <: Foo[X] }]
(не обращая внимания на мгновение тот факт, что последний даже не является допустимым типом). Тогда мы смогли бы написать следующее:
myFoo: Foo[X forSome { type X <: Foo[X] }]
Но Foo
инвариантно, поэтому, если есть вещь, которая является экземпляром как Foo[A]
и Foo[B]
, то это должно быть так, что A =:= B
. Но это определенно не тот случай, когда MyFoo =:= (X forSome { type X <: Foo[X] })
.Не уверен, что все это путано, но я убедился, что компилятор знает, что он здесь делает.
Я не понимаю, почему 'X forSome {type X <: Enum [X]}' не является подтипом 'Enum [X forSome {type X <: Enum [X]}]' (на мой взгляд, 'X <: Enum [X] ', и те же ограничения применяются рекурсивно). Вы можете объяснить? Неужели мы сталкиваемся с одним из странных ограничений F-связанного полиморфизма? – gzm0
@ gzm0: Помогло ли мое обновление? –