2016-11-28 3 views
3

Est-il possible de résoudre un paramètre implicite pour un type B si un type implicite est défini pour son super type A?Résoudre le paramètre implicite du super type

Voici un exemple:

J'ai une Enumerable classe de types:

trait Enumerable[A] { 

    def name(a: A): String 

    def list: List[A] 

    //... other methods 
} 

object Enumeration { 
    def name[A, T >: A](a: A)(implicit ev: Enumerable[T]) = ev.name(a) 

    def list[T](implicit ev: Enumerable[T]) = ev.list 

    // ... 
} 

Je définir une instance de dénombrable:

sealed trait Season 

case object Winter extends Season 
case object Spring extends Season 
case object Summer extends Season 
case object Fall extends Season 

implicit val seasonEnumerable = new Enumerable[Season] { 
    override def list: List[Season] = List(Winter, Spring, Summer, Fall) 
} 

// working : 
Enumeration.name(Winter: Season) shouldBe "winter" 

// faling : 
Enumeration.name(Winter) shouldBe "winter" 

Enumeration.name (hiver) échoue si Je ne dis pas au scalac que l'hiver est une saison. J'ai spécifié que le paramètre implicite dans la signature de la méthode 'name' est un supertype de A, mais ce n'est pas suffisant ...

Y a-t-il une meilleure façon de procéder?

Répondre

3

La réponse de Eduardo explique pourquoi la version avec [A, T >: A] ne fonctionne pas. Mais il y a une solution simple au problème qu'il donne: au lieu d'introduire T déduisent comme paramètre de type, l'introduire par un type existentiel:

def name[A](a: A)(implicit ev: Enumerable[T >: A] forSome { type T }) = ev.name(a) 

Ou, en utilisant un raccourci,

def name[A](a: A)(implicit ev: Enumerable[_ >: A]) = ev.name(a) 

Ensuite, le compilateur doit seulement décider ce que T est en recherchant ev.

+0

En effet! Merci :) Pourriez-vous expliquer la différence entre def nom [A] (a: A) (implicite ev: Enumerable [_>: A]) = ev.nom (a) et def nom [A, T>: A] (a: A) (implicite ev: Enumerable [T]) = ev.nom (a)? – Loic

+1

J'ai développé la réponse. –

3

Ceci est un inconvénient commun lorsque vous avez besoin de types dépendant du type à déduire. Votre méthode

def name[A, T >: A](a: A)(implicit ev: Enumerable[T]) 

quand appelé Winter, premier A sera déduit à Winter.type et T être A, car il est conforme à cette limite et il y a des contraintes pas plus sur elle à ce moment-là. Alors bien sûr, le compilateur ne trouvera pas une instance de Enumerable[Winter.type].

Il y a une solution facile avec les membres de type cependant:

trait AnyEnumerable { 

    type E 

    def name[A <: E](a: A): String 
    def list: List[E] 
} 

object Enumeration { 

    def name[A](a: A)(implicit ev: AnyEnumerable { type E >: A }) = ev.name(a) 
    def list[T](implicit ev: AnyEnumerable { type E = T }) = ev.list 
    // ... 
} 

// an implicit for `Season` 
implicit val seasonEnumerable: AnyEnumerable { type E = Season } = 
    new AnyEnumerable { 

    type E = Season 

    def name[A <: Season](a: A): String = a.toString 
    def list: List[Season] = List(Winter, Spring, Summer, Fall) 
    } 

// compiles! 
val zzz = Enumeration.name(Winter)