为什么这个隐式解决方案失败了?

Why is this implicit resolution failing?

我有一个隐式转换 - 如下 - 感觉它肯定应该工作但绝对不是。任何人都可以阐明吗?我知道 implicitly 在使用类型优化时有时会失败 - 问题出在这里吗?

trait GetItem[A[_], T, R] {
  type Out
  def ret(a: A[T], ref: R): Out
}
object GetItem {
  implicit def ifRefIsInt[A[_], T]: GetItem[A, T, Int] { type Out = A[T] } = new GetItem[A, T, Int] {
    type Out = A[T]
    def ret(a: A[T], ref: Int): Out = a
  }
}
import GetItem._
//this works fine:
val t: GetItem[List, Double, Int] { type Out = List[Double] } = ifRefIsInt[List, Double]
// so does this:
implicitly[GetItem[List, Double, Int] { type Out = List[Double] }](t)
// this does not:
implicitly[GetItem[List, Double, Int] { type Out = List[Double] }] 
// Could not find implicit parameter for value e: Example.Main.GetItem[List, Double, Int]{type Out = List[Double]}

非常感谢任何帮助,我已经盯着这个看了一段时间但收效甚微。

不知道为什么它不能那样工作,但是解决这类问题的一个好方法是使用 Aux 模式来提升 将成员 键入 类型参数 ,从而提高分辨率。

trait GetItem[A[_], T, R] {
  type Out
  def ret(a: A[T], ref: R): Out
}

object GetItem {
  type Aux[A[_], T, R, O] = GetItem[A, T, R] { type Out = O }

  implicit def ifRefIsInt[A[_], T]: Aux[A, T, Int, A[T]] = new GetItem[A, T, Int] {
    type Out = A[T]
    def ret(a: A[T], ref: Int): Out = a
  }
}

你可以这样测试:

implicitly[GetItem.Aux[List, Double, Int, List[Double]]]
// res: GetItem.Aux[List, Double, Int, List[Double]] = ...

implicitly[GetItem[List, Double, Int]] 
// res: GetItem[List, Double, Int] = ...

似乎是过度约束隐式的另一个例子 ( 2 3 )。对于一步中的隐式来说,这似乎工作量太大了。编译器不喜欢类型细化中的复杂类型,如 (A, B)H :: LA[T](在我们的例子中)。如果我们替换

implicit def ifRefIsInt[A[_], T]: GetItem[A, T, Int] { type Out = A[T] } = 
  new GetItem[A, T, Int] {
    type Out = A[T]
    def ret(a: A[T], ref: Int): Out = a
  }

implicit def ifRefIsInt[A[_], T, O](implicit 
  ev: A[T] =:= O
): GetItem[A, T, Int] { type Out = O } = new GetItem[A, T, Int] {
  type Out = O
  def ret(a: A[T], ref: Int): Out = a
}

然后

implicitly[GetItem.Aux[List, Double, Int, List[Double]]]
implicitly[GetItem[List, Double, Int] { type Out = List[Double] }]

编译:https://scastie.scala-lang.org/P5iXP2ZfQUCKEIMukYyqIg (Scala 2.13.3)

出于某种原因,编译器吞下了警告(-Xlog-implicits 已打开)。如果我手动触发隐式搜索

import scala.language.experimental.macros
import scala.reflect.macros.{whitebox, contexts}

def foo[A]: Unit = macro fooImpl[A]

def fooImpl[A: c.WeakTypeTag](c: whitebox.Context): c.Tree = {
  import c.universe._

  val context = c.asInstanceOf[contexts.Context]
  val global: context.universe.type = context.universe
  val analyzer: global.analyzer.type = global.analyzer
  val callsiteContext = context.callsiteTyper.context

  val typ = weakTypeOf[A]

  val search = new analyzer.ImplicitSearch(
    tree = EmptyTree.asInstanceOf[global.Tree],
    pt = typ.asInstanceOf[global.Type],
    isView = false,
    context0 = callsiteContext.makeImplicit(reportAmbiguousErrors = true),
    pos0 = c.enclosingPosition.asInstanceOf[scala.reflect.internal.util.Position]
  )

  println(s"allImplicits=${search.allImplicits}")

  q""
}

然后

foo[GetItem[List, Double, Int] { type Out = List[Double] }]

产生警告

App.this.GetItem.ifRefIsInt is not a valid implicit value for App.GetItem[List,Double,Int]{type Out = List[Double]} because:
hasMatchingSymbol reported error: polymorphic expression cannot be instantiated to expected type;
 found   : [A[_], T]App.GetItem[A,T,Int]{type Out = A[T]}
 required: App.GetItem[List,Double,Int]{type Out = List[Double]}

scalac: allImplicits=List()

AT 未推断。