Scala: implicit type class instance lookup 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))
}

Call api.doSomethingnot being made:

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

If I change the signature of the constructor class Api[T]so that it accepts ConcreteConfig, then the compiler can say that the config.Repr == Stringimplicit search completes successfully. But this will not work for my use case.

Is there any other way to do an implicit search? Am I losing type information because I am missing a type clarification or something else?

+4
source share
2 answers

This cannot be achieved because config.Repr is not a stable path. The compiler cannot determine that config.Repr = String, although the configuration runtime is of type 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))
} 
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))
}
0

Source: https://habr.com/ru/post/1660637/


All Articles