2014-09-10 6 views
16

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

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]).

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

ответ

2

Я не понимаю, почему его работа, но следующий код успешно разрешает имплицитно в текущем случае (по крайней мере, на 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]] 
+0

решение работает для данный пример. Но в моем случае я использую 'Generic', где используется' GiveMeJustA'. И в этом случае решение, похоже, не работает, я думаю, из-за макросов. – tksfz