Создание экземпляров класса ковариантного типа из экземпляров нековариантного класса

Предположим, у меня есть класс простого типа, экземпляры которого дадут мне значение некоторого типа:

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] = GiveMeA$$anon$1@2a4f2dcc

И это несмотря на то, что мы не можем применить GiveMeA.fromGiveMeJustA к этому экземпляру, чтобы получить GiveMeA[Foo] (или любой подтип GiveMeA[Foo]).

Мне это кажется ошибкой, но, возможно, я что-то упускаю. Есть ли в этом смысл? Есть ли разумный обходной путь?


person Travis Brown    schedule 10.09.2014    source источник


Ответы (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

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]]
person mucaho    schedule 13.09.2014
comment
Решение работает для данного примера. Но в моем случае я использую Generic, где используется GiveMeJustA. И в этом случае решение, похоже, не работает, я полагаю, из-за макросов. - person tksfz; 29.11.2016