2016-11-13 83 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调用失败编译:斯卡拉:类型类实例的隐式的查找路径依赖型

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

如果我改变class Api[T]的构造方法的签名,这样只有一个ConcreteConfig ,那么编译器可以知道config.Repr == String和隐式查找成功。但是这对我的用例并不适用。

是否有任何其他方式来指导隐式查找?我是否因为缺少类型优化或某种东西而丢失了类型信息?

回答

0

由于config.Repr不是一个稳定的路径,因此无法实现。尽管config的运行时值是MyConfig类型,但编译器无法确定config.Repr = String。

这将只工作,如果你能提供具体的配置实例作为阿比类型参数,它会告诉编译器什么的确切类型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

推断类型一步一个时间。此外,我没有看到在这里使用依赖于值的类型的任何理由(或者对于那个地方,尽管这是一个不同的问题)。

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