2014-09-10 9 views
16

Angenommen, ich habe eine einfache Art Klasse, deren Instanzen bekam gibt mir einen Wert von etwas Art:Erstellen von Instanzen einer kovarianten Typklasse von Instanzen eines nicht covariant ein

trait GiveMeJustA[X] { def apply(): X } 

Und ich habe einige Fälle:

case class Foo(s: String) 
case class Bar(i: Int) 

implicit object GiveMeJustAFoo extends GiveMeJustA[Foo] { 
    def apply() = Foo("foo") 
} 

implicit object GiveMeJustABar extends GiveMeJustA[Bar] { 
    def apply() = Bar(13) 
} 

Jetzt habe ich eine ähnliche (aber nicht verwandten) Klasse geben, die das gleiche tut, aber in seiner Art Parameter kovariant:

trait GiveMeA[+X] { def apply(): X } 

In seinem Begleitobjekt sagen wir dem Compiler, wie Instanzen von Instanzen unserer nicht-kovarianten Typklasse erstellen:

object GiveMeA { 
    implicit def fromGiveMeJustA[X](implicit giveMe: GiveMeJustA[X]): GiveMeA[X] = 
    new GiveMeA[X] { def apply() = giveMe() } 
} 

Jetzt würde ich implicitly[GiveMeA[Foo]] erwarte einfach gut zu kompilieren, da eine Möglichkeit gibt es nur eine bekommen GiveMeA[Foo] gegeben die Stücke, die wir hier haben. Aber es funktioniert nicht (zumindest nicht auf entweder 2.10.4 oder 2.11.2):

scala> implicitly[GiveMeA[Foo]] 
<console>:16: this.GiveMeA.fromGiveMeJustA is not a valid implicit value for GiveMeA[Foo] because: 
hasMatchingSymbol reported error: ambiguous implicit values: 
both object GiveMeJustAFoo of type GiveMeJustAFoo.type 
and object GiveMeJustABar of type GiveMeJustABar.type 
match expected type GiveMeJustA[X] 
       implicitly[GiveMeA[Foo]] 
         ^
<console>:16: error: could not find implicit value for parameter e: GiveMeA[Foo] 
       implicitly[GiveMeA[Foo]] 
         ^

Wenn wir unsere irrelevant GiveMeJustA Instanz loszuwerden, es funktioniert:

scala> implicit def GiveMeJustABar: List[Long] = ??? 
GiveMeJustABar: List[Long] 

scala> implicitly[GiveMeA[Foo]] 
res1: GiveMeA[Foo] = [email protected] 

Dies ist in Trotz der Tatsache, dass es keine Möglichkeit gibt, GiveMeA.fromGiveMeJustA auf diese Instanz anzuwenden, um eine GiveMeA[Foo] (oder einen beliebigen Subtyp von GiveMeA[Foo]) zu erhalten.

Das sieht für mich wie ein Fehler aus, aber es ist möglich, dass ich etwas vermisse. Macht das irgendeinen Sinn? Gibt es eine angemessene Problemumgehung?

Antwort

2

Ich verstehe nicht, warum es funktioniert, aber der folgende Code löst die implizit erfolgreich im aktuellen Fall (zumindest auf scala v-2.10.1). Dies ist jedoch noch nicht erklärt, warum Ihr Beispiel ist nicht in erster Linie arbeiten:

Wir die implizite GiveMeA[X] Instanz ändern für implizite GiveMeJustA Instanzen zu suchen, wo die Typ-Parameter nach oben durch X begrenzt sind, so sucht es nach GiveMeJustA[_ <: X]

object GiveMeA { 
    implicit def fromGiveMeJustA[X](implicit giveMe: GiveMeJustA[_ <: X]) : GiveMeA[X] = 
    new GiveMeA[X] { def apply() = giveMe() } 
} 

Wir können dann

val a = implicitly[GiveMeA[Foo]] 
println(a()) // prints "Foo(foo)" 

aus dem erwarteten Ausgang drucken

jedoch, sobald wir eine neue Unterklasse

case class FooChild(s: String) extends Foo(s) 

und die jeweilige GiveMeJustA typeclass Instanz

implicit object GiveMeJustAFooChild extends GiveMeJustA[FooChild] { 
    def apply() = FooChild("fooChild") 
} 

der Compiler beschwert sich (wie erwartet)

error: could not find implicit value for parameter e: GiveMeA[Foo] 
    val a = implicitly[GiveMeA[Foo]] 
+0

Die Lösung arbeitet für die Einführung gegebenes Beispiel. Aber in meinem Fall verwende ich 'Generic', wo' GiveMeJustA' verwendet wird. Und in diesem Fall scheint die Lösung nicht zu funktionieren, ich vermute wegen der Makros. – tksfz

Verwandte Themen