为什么Scala的隐式查找会忽略嵌套类的同伴对象

时间:2018-07-27 22:16:44

标签: scala nested implicit companion-object path-dependent-type

我在玩以下代码:

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[_](或类似的类型),这并不是编译器正在寻找的。我还知道,将案例类Codec[_]及其同伴移到特征之外可以解决此问题(在使其具有2类参数之后)。如果需要隐式值,则编译器应在隐式范围内包括所需类型的伴随对象。我的问题是:

  1. 如何将编译器指向与路径相关的子类型的伴侣,更具体地说:
  2. 是否可以更改trait的两个方法的签名(最好是更改隐式参数的类型签名)以进行编译?如何从特征Acodecs.Codec[_]内部引用类型Codecs[_]
  3. 就像,您如何在嵌套类型上执行此类型类操作?

  4. 是否存在某种模式或某种可以解决此类问题的东西?

1 个答案:

答案 0 :(得分:2)

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

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

您收到以下错误消息:

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

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

我从未真正看到过这种特定类型的隐式嵌套-即其中包含与路径相关的隐式类型类的隐式类型类。因此,我怀疑是否存在处理它的模式,并且实际上会对它提出建议。似乎过于复杂。这是我个人对待此案的方法:

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)
}

您仍然可以使用“半缩窄”类型的好处,但这只是类型别名,因此不存在路径依赖问题。