为什么 scala 的隐式查找会忽略嵌套 class 的伴生对象

why does scala's implicit lookup ignore companion object of nested class

我正在玩弄以下代码:

class A
class B
class C

trait Codecs[L] {
    case class Codec[R](val code: L => R, val decode: R => L)
    object Codec

    def code[R](foo: L)(implicit codec: Codec[R]): R = codec.code(foo)
    def decode[R](bar: R)(implicit codec: Codec[R]): L = codec.decode(bar)
}

object Codecs {
    implicit object ACodecs extends Codecs[A] {
        object Codec {
            implicit val ab: Codec[B] = new Codec(_ => new B, _ => new A)
            implicit val ac: Codec[C] = new Codec(_ => new C, _ => new A)
        }
    }
}

object test extends App {
    val codecs = implicitly[Codecs[A]]
    codecs.code[B](new A)
} 

它不会编译,因为编译器无法找到类型 Codecs.Codec[B] 的隐式值。 据我了解,abac 这两个值的类型是 Acodecs.Codec[_](或类似的东西),这并不是编译器正在寻找的。我也知道将 case class Codec[_] 及其同伴移到特征之外可以解决问题(在使其采用 2 个类型参数之后)。如果需要隐式值,编译器应在隐式范围内包含所需类型的伴生对象。我的问题是:

  1. 如何将编译器指向依赖于路径的子类型,更具体地说:
  2. 是否可以更改 trait 的两个方法的签名(最好更改隐式参数的类型签名)以进行编译?如何从特征 Codecs[_] 内部引用类型 Acodecs.Codec[_]
  3. 比如,你如何在嵌套类型上做这种类型class的事情?

  4. 是否有处理此类问题的模式或东西?

问题是您的类型绑定到特定实例,因为它是内部 class。并且编译器不知道 implicitly[Codecs[A]] 给出的实例与它在下一行隐式找到的实例完全相同。例如,如果您显式传递它:

codecs.code[B](new A)(Codecs.ACodecs.Codec.ab)

您收到此错误消息:

type mismatch;
 found   : Codecs.ACodecs.Codec[B]
 required: codecs.Codec[B]

因此它认为封闭的实例可能不同,因此类型不同。

我从未真正见过这种特定类型的隐式嵌套——即隐式类型class,其中包含路径相关的隐式类型classes。所以我怀疑是否存在处理它的模式,实际上我会建议不要这样做。这似乎过于复杂。以下是我个人如何处理这个案例:

case class Codec[L, R](val code: L => R, val decode: R => L)

trait Codecs[L] {
  type LocalCodec[R] = Codec[L, R]

  def code[R](foo: L)(implicit codec: LocalCodec[R]): R = codec.code(foo)
  def decode[R](bar: R)(implicit codec: LocalCodec[R]): L = codec.decode(bar)
}

object Codecs {
  implicit object ACodecs extends Codecs[A] {
    implicit val ab: LocalCodec[B] = new LocalCodec(_ => new B, _ => new A)
    implicit val ac: LocalCodec[C] = new LocalCodec(_ => new C, _ => new A)
  }
}

object test extends App {
  import Codecs.ACodecs._
  val codecs = implicitly[Codecs[A]]    
  codecs.code[B](new A)
}

您仍然可以使用 "half-narrowed" 类型,但它只是一个类型别名,因此不存在路径依赖性问题。