Создание экземпляров класса ковариантного типа из экземпляров нековариантного
Предположим, у меня есть простой класс типа, экземпляры которого дадут мне значение некоторого типа:
trait GiveMeJustA[X] { def apply(): X }
И у меня есть несколько экземпляров:
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)
}
Теперь у меня есть аналогичный (но несвязанный) тип, который делает то же самое, но ковариантно в своем параметре типа:
trait GiveMeA[+X] { def apply(): X }
В своем сопутствующем объекте мы сообщаем компилятору, как создавать экземпляры из экземпляров нашего несовместимого типа:
object GiveMeA {
implicit def fromGiveMeJustA[X](implicit giveMe: GiveMeJustA[X]): GiveMeA[X] =
new GiveMeA[X] { def apply() = giveMe() }
}
Теперь я ожидал бы, что implicitly[GiveMeA[Foo]]
будет компилироваться просто отлично, так как есть только один способ получить GiveMeA[Foo]
с учетом частей, которые у нас есть. Но это не (по крайней мере, не на 2.10.4 или 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]]
^
Если мы избавимся от нашего нерелевантного экземпляра GiveMeJustA
, он работает:
scala> implicit def GiveMeJustABar: List[Long] = ???
GiveMeJustABar: List[Long]
scala> implicitly[GiveMeA[Foo]]
res1: GiveMeA[Foo] = [email protected]
Это несмотря на то, что мы не можем применить GiveMeA.fromGiveMeJustA
к этому экземпляру, чтобы получить GiveMeA[Foo]
(или любой подтип GiveMeA[Foo]
).
Это выглядит как ошибка для меня, но возможно, что я что-то упустил. Есть ли в этом смысл? Существует ли разумное решение?
Ответы
Ответ 1
Я не понимаю, почему его работа, но следующий код разрешает имплицитно успешно в текущем случае (по крайней мере, на scala v-2.10.1). Однако это все еще не объясняет, почему ваш пример не работает в первую очередь:
Мы заменяем неявный GiveMeA[X]
экземпляр для поиска неявных экземпляров GiveMeJustA
, где параметр типа ограничивается вверх X
, поэтому он ищет GiveMeJustA[_ <: X]
object GiveMeA {
implicit def fromGiveMeJustA[X](implicit giveMe: GiveMeJustA[_ <: X]) : GiveMeA[X] =
new GiveMeA[X] { def apply() = giveMe() }
}
Затем мы можем распечатать ожидаемый вывод
val a = implicitly[GiveMeA[Foo]]
println(a()) // prints "Foo(foo)"
Однако, как только мы введем новый подкласс
case class FooChild(s: String) extends Foo(s)
и соответствующий GiveMeJustA
экземпляр typeclass
implicit object GiveMeJustAFooChild extends GiveMeJustA[FooChild] {
def apply() = FooChild("fooChild")
}
компилятор жалуется (как и ожидалось)
error: could not find implicit value for parameter e: GiveMeA[Foo]
val a = implicitly[GiveMeA[Foo]]