关于scala:定义第三方类型类的实例,找不到隐式但显式工作正常

Defining instances of a third-party typeclass, implicit not found but explicit works fine

我正在使用Slick的GetResult类型类,并希望使用Shapeless派生GetResult[Option[(A, B, C...)]]

的实例

我想要的是:

给出一个隐式的GetResult[Option[A]], GetResult[Option[B]], ...
隐式生成GetResult[Option[(A, B, ...)]]

我尝试了什么

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
trait CanGetOption[T] {
    def getOption: GetResult[Option[T]]
}
object CanGetOption {
    // convenience implicit resolver
    def apply[T](implicit canGetOption: CanGetOption[T]): CanGetOption[T] = canGetOption

    // base case: HNil
    implicit val getHNilOption: CanGetOption[HNil] = from(GetResult { _ => Some(HNil) })

    // recursion case: H :: Tail
    implicit def getHConsOption[H, Tail <: HList](
        implicit getHeadOption: GetResult[Option[H]],
        canGetTailOption: CanGetOption[Tail]
    ): CanGetOption[H :: Tail] = from(GetResult[Option[H :: Tail]] { r =>
        val headOpt = getHeadOption(r)
        val tailOpt = canGetTailOption.getOption(r)
        for(head <- headOpt; tail <- tailOpt) yield head :: tail
    })

    // generic case: A, given a A <-> Repr conversion
    // I also tried moving this into a"LowPriorityImplicits" thing, just in case
    implicit def getGenericOption[A, Repr <: HList](
        implicit gen: Generic.Aux[A, Repr],
        getReprOpt: CanGetOption[Repr]
    ): CanGetOption[A] = from(GetResult { r =>
        val reprOpt = getReprOpt.getOption(r)
        reprOpt.map(gen.from)
    })
}

implicit def resolveOptionGetter[T: CanGetOption]: GetResult[Option[T]] =
    CanGetOption[T].getOption

问题:

当我导入上面的内容时,在搜索隐式对象时似乎没有考虑resolveOptionGetter

1
2
3
4
5
6
7
scala> implicitly[GetResult[Option[(Int, Int)]]]
<console>:19: error: could not find implicit value for parameter e: scala.slick.jdbc.GetResult[Option[(Int, Int)]]
       implicitly[GetResult[Option[(Int, Int)]]]
             ^

scala> resolveOptionGetter[(Int, Int)]
res1: scala.slick.jdbc.GetResult[Option[(Int, Int)]] = <function1>

为什么编译器在隐式搜索中找不到resolveOptionGetter?我该怎么办?


问题是slick.jdbc.GetResult是协变的。如果它是不变的,则将正确推断类型并解析隐式。

一种解决方法是使用自定义不变类型别名GetResult隐藏协变slick.jdbc.GetResult。删除导入slick.jdbc.GetResult并写入您的源文件

1
2
3
4
5
type GetResult[T] = slick.jdbc.GetResult[T]

object GetResult {
  def apply[T](implicit f: PositionedResult => T): GetResult[T] = slick.jdbc.GetResult.apply
}

现在implicitly[GetResult[Option[(Int, Int)]]]进行编译。已在Scala 2.12.7中进行测试,无变形2.3.3光滑3.2.3,

方差通常会给隐式解析带来麻烦:

https://github.com/scala/bug/issues/10099

https://github.com/locationtech/geotrellis/issues/1292

具有协方差的隐式分辨率