Scala:隐式查找类型 class 路径相关类型的实例

Scala: implicit lookup of type class instance for path-dependent type

trait Encoder[From, To] {
  def encode(x: From): To
}
object Encoder {
  implicit val thingToString: Encoder[Thing, String] = new Encoder[Thing, String] {
    def encode(x: Thing): String = x.toString
  }
}

trait Config {
  type Repr
}
class MyConfig extends Config { type Repr = String }
//class ConcreteConfig { type Repr = String }

class Api[T](val config: Config) {
  def doSomething(value: T)(implicit encoder: Encoder[T, config.Repr]): Unit = {}
}

case class Thing(a: Int)

object Test extends App {
  import Encoder._

  val api = new Api[Thing](new MyConfig)
  api.doSomething(Thing(42))
}

调用api.doSomething编译失败:

could not find implicit value for parameter encoder: Encoder[Thing,Test.api.config.Repr]

如果我更改 class Api[T] 的构造函数的签名,使其采用 ConcreteConfig,则编译器可以判断 config.Repr == String 并且隐式查找成功。但这对我的用例来说并不适用。

有没有其他方法可以引导隐式查找?我是否丢失了类型信息,因为我缺少类型优化之类的?

这无法实现,因为 config.Repr 不是稳定的路径。 编译器无法确定 config.Repr = String,即使config 的运行时值是 MyConfig 类型。

只有当您可以在 Api 中提供具体的配置实例作为类型参数时,这才会起作用,这将告诉编译器 Repr 的确切类型是什么:

class Api[T, C <: Config](val config: C) {
  def doSomething(value: T)(implicit encoder: Encoder[T, config.Repr]): Unit = {}
}

object Test extends App {
  import Encoder._

  val api = new Api[Thing, MyConfig](new MyConfig)
  api.doSomething(Thing(42))
} 

推断类型一次一个步骤。此外,我看不出有任何理由在这里(或者,就此而言,在任何地方;尽管这是一个不同的问题)使用依赖于值的类型。

trait Encoder[From, To] {
  def encode(x: From): To
}
object Encoder {

  implicit val thingToString: Encoder[Thing, String] = new Encoder[Thing, String] {
    def encode(x: Thing): String = x.toString
  }
}

trait Config {
  type Repr
}
class MyConfig extends Config { type Repr = String }
// class ConcreteConfig { type Repr = String }

case class Api[T, C <: Config](val config: C) {
  def doSomething(value: T)(implicit encoder: Encoder[T, C#Repr]): Unit = ()
}

case class ApiFor[T]() {
  def withConfig[C <: Config](config: C): Api[T,C] = Api(config)
}

case class Thing(a: Int)

object Test extends App {
  import Encoder._

  val api = ApiFor[Thing] withConfig new MyConfig
  // compiles!
  api.doSomething(Thing(42))
}