2013-08-23 14 views
8

Nieco mylące dotyczące typów egzystencjalnych.Typy egzystencjalne w Scali

Działa to dla mnie:

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

ale to nie:

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

W moim umyśle oba wyrażenia są równoważne:

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

Ale Scala mówi, że jest to w tylko mój umysł:

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

Odpowiedz

5

Rozważmy różnicę między dwóch poniższych wyrażeń:

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

oraz:

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

Teraz pomyśl o tym, jak parametr typu T od valueOf będzie wnioskować w każdym z tych przypadków. W pierwszym przypadku mamy X, który, jak wiemy, jest podtypem Enum[X], a my wszyscy jesteśmy ustawieni. W drugim przypadku, z drugiej strony, T musiałby być X forSome { type X <: Enum[X] }, a co najważniejsze, ten typ nie jest podtypem Enum[X forSome { type X <: Enum[X] }], więc nie spełniamy ograniczenia na T.

Problem polega na tym, że twój drugi przykład jest równoważny temu drugiemu.


jako przypis, to będzie działać dobrze, jeśli Enum zostały kowariantna w jego parametr typu. Weźmy następujący uproszczony przykład:

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] }] = ??? 

Teraz foo(x) skompiluje, ale foo(y) nie będzie, tak jak w kodzie. Ale zmień nieco: Bar bit:

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] }] = ??? 

Teraz obie się skompilują. Sądzę, że ma to coś wspólnego z tym, że mamy tak silną intuicję, że twoje dwa przykłady są równoważne.


W innym przypisie (w odpowiedzi na gzmo „s comment below), należy rozważyć następujące kwestie:

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] 

Załóżmy, że X forSome { type X <: Foo[X] }były podtypem Foo[X forSome { type X <: Foo[X] }] (pomijając na chwilę fakt, że ten ostatni nie jest nawet poprawnym typem). Wtedy będziemy mogli napisać następujące:

myFoo: Foo[X forSome { type X <: Foo[X] }] 

Ale Foo jest niezmienna, więc jeśli mamy jakąś rzecz, która jest instancją zarówno Foo[A] i Foo[B], to musi być tak, że A =:= B. Ale na pewno nie jest tak, że MyFoo =:= (X forSome { type X <: Foo[X] }).Nie jestem pewien, czy to wszystko jest mniej kłopotliwe, ale przekonałem się, że kompilator wie, co tu robi.

+0

Nie rozumiem, dlaczego 'X forSome {type X <: Enum [X]}' nie jest podtypem 'Enum [X forSome {type X <: Enum [X]}]' (w mojej głowie, 'X <: Enum [X] 'i te same ograniczenia mają zastosowanie rekursywnie). Możesz wytłumaczyć? Czy napotykamy jedno z dziwnych ograniczeń polimorfizmu związanego z F? – gzm0

+0

@ gzm0: Czy moja aktualizacja pomaga? –