2016-11-28 3 views
3

Kann ein impliziter Parameter für einen Typ B aufgelöst werden, wenn für seinen Super-Typ A ein implizites definiert ist? HierImplizite Parameter vom Super-Typ auflösen

ist ein Beispiel:

Ich habe eine Enumerable typeclass:

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 

    // ... 
} 

Dann definiere ich eine Instanz von enumerable:

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 (Winter) ist versagt, wenn Ich sage Scalac nicht, dass der Winter eine Jahreszeit ist. Ich habe angegeben, dass der implizite Parameter in der "Name" -Methodensignatur ein Supertyp von A ist, aber es ist nicht ausreichend ...

Gibt es einen besseren Weg, dies zu tun?

Antwort

3

Eduardo Antwort erklärt, warum die Version mit [A, T >: A] funktioniert nicht. Aber es gibt eine einfachere Lösung für das Problem, als er gibt: statt T infer als Typ-Parameter Einführung Einführung durch eine existentielle Art:

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

Oder eine Kurz verwenden,

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

Dann muss der Compiler nur entscheiden, was T ist, wenn er nach ev sucht.

+0

In der Tat! Danke :) Könnten Sie den Unterschied zwischen definieren def name [A] (a: A) (implizit ev: Enumerable [_>: A]) = ev.name (a) und def name [A, T>: A] (a: A) (implizit ev: Enumerable [T]) = ev.name (a)? – Loic

+1

Ich habe die Antwort erweitert. –

3

Dies ist eine häufige Unannehmlichkeit, wenn typabhängige Typen abgeleitet werden müssen. Ihre Methode

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

wenn auf Winter genannt, ersten A wird Winter.type abgeleitet wird und dann TA zu sein, wie es zu dieser Grenze entspricht und es keine weiteren Einschränkungen für sie an diesem Punkt. Dann wird der Compiler keine Instanz von Enumerable[Winter.type] finden.

Es gibt eine einfache Lösung mit Typ-Mitgliedern aber:

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) 
Verwandte Themen