2016-11-13 4 views
4
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 не может скомпилировать:Scala: неявный поиск экземпляра класса типа для пути в зависимости от типа

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

Если изменить подпись конструктора class Api[T] «s так, что она занимает ConcreteConfig , то компилятор может сказать, что config.Repr == String и неявный поиск завершается успешно. Но это не будет работать для моего случая использования.

Есть ли другой способ вести неявный поиск? Я теряю информацию о типе, потому что мне не хватает уточнения типа или чего-то еще?

ответ

0

Этого не может быть сделано, так как config.Repr не является устойчивым путем. Компилятор не может определить, что config.Repr = String, хотя время выполнения конфигурации имеет тип MyConfig.

Это будет работать только если вы можете предоставить конкретный пример конфигурации в качестве параметра типа в Апи, который расскажет компилятор, что это точный тип 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

'api.config.Repr'_is_ стабильный путь, хотя ваше второе предложение является правильным. –

0

типа Infer один шаг за один раз , Кроме того, я не вижу причин для использования зависимых от стоимости типов (или, если на то пошло, в любом месте, но это другой вопрос).

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