Scala:为路径依赖类型隐式查找类型类实例

时间:2016-11-13 20:55:49

标签: scala implicit 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并且隐式查找成功。但这对我的用例来说并不适用。

还有其他方法来指导隐式查找吗?我丢失了类型信息,因为我错过了类型细化或什么?

2 个答案:

答案 0 :(得分:0)

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

答案 1 :(得分:0)

一次推断类型一步。此外,我没有看到在这里使用价值相关类型的任何理由(或者,就此而言,在任何地方;但这是一个不同的问题)。

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