scala - reservadas - Creación de instancias de una clase de tipo covariante a partir de instancias de una clase no covariante
self type scala (1)
Supongamos que tengo una clase de tipo simple cuyas instancias me darán un valor de algún tipo:
trait GiveMeJustA[X] { def apply(): X }
Y tengo algunos casos:
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)
}
Ahora tengo una clase de tipo similar (pero no relacionada) que hace lo mismo pero es covariante en su parámetro de tipo:
trait GiveMeA[+X] { def apply(): X }
En su objeto complementario, le decimos al compilador cómo crear instancias a partir de instancias de nuestra clase de tipo no covariante:
object GiveMeA {
implicit def fromGiveMeJustA[X](implicit giveMe: GiveMeJustA[X]): GiveMeA[X] =
new GiveMeA[X] { def apply() = giveMe() }
}
Ahora esperaría implicitly[GiveMeA[Foo]]
compile bien, ya que solo hay una manera de obtener GiveMeA[Foo]
dadas las piezas que tenemos aquí. Pero no lo hace (al menos no en 2.10.4 o 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]]
^
Si nos deshacemos de nuestra instancia irrelevante de GiveMeJustA
, funciona:
scala> implicit def GiveMeJustABar: List[Long] = ???
GiveMeJustABar: List[Long]
scala> implicitly[GiveMeA[Foo]]
res1: GiveMeA[Foo] = GiveMeA$$anon$1@2a4f2dcc
Esto es a pesar del hecho de que no hay manera de que podamos aplicar GiveMeA.fromGiveMeJustA
a esta instancia para obtener un GiveMeA[Foo]
(o cualquier subtipo de GiveMeA[Foo]
).
Esto me parece un error, pero es posible que me esté perdiendo algo. ¿Tiene esto algún sentido? ¿Hay una solución razonable?
No entiendo por qué funciona, pero el siguiente código resuelve lo implícito con éxito en el caso actual (al menos en scala v-2.10.1). Sin embargo, esto todavía no explica por qué su ejemplo no funciona en primer lugar:
Cambiamos la GiveMeA[X]
implícita para GiveMeJustA
instancias de GiveMeJustA
implícitas donde el parámetro de tipo está delimitado por X
, por lo que busca GiveMeJustA[_ <: X]
object GiveMeA {
implicit def fromGiveMeJustA[X](implicit giveMe: GiveMeJustA[_ <: X]) : GiveMeA[X] =
new GiveMeA[X] { def apply() = giveMe() }
}
Luego podemos imprimir el resultado esperado.
val a = implicitly[GiveMeA[Foo]]
println(a()) // prints "Foo(foo)"
Sin embargo, tan pronto como introducimos una nueva subclase.
case class FooChild(s: String) extends Foo(s)
y la respectiva instancia de clase de GiveMeJustA
GiveMeJustA
implicit object GiveMeJustAFooChild extends GiveMeJustA[FooChild] {
def apply() = FooChild("fooChild")
}
el compilador se queja (como se espera)
error: could not find implicit value for parameter e: GiveMeA[Foo]
val a = implicitly[GiveMeA[Foo]]