解释 Scala 类型级编程中使用的“LowPriorityImplicits”模式
Explain the `LowPriorityImplicits` pattern used in Scala type-level programming
查看某些 Scala 库的源代码时,例如shapeless,我经常发现名为 LowPriorityImplicits
的特征。
你能解释一下这个模式吗?解决的问题是什么,pattern是怎么解决的?
该模式允许您拥有隐式层次结构,避免编译器出现与歧义相关的错误,并提供一种方法来确定它们的优先级。例如考虑以下内容:
trait MyTypeclass[T] { def foo: String }
object MyTypeclass {
implicit def anyCanBeMyTC[T]: MyTypeclass[T] = new MyTypeclass[T] {
val foo = "any"
}
implicit def specialForString[T](implicit ev: T <:< String): MyTypeclass[T] = new MyTypeclass[T] {
val foo = "string"
}
}
println(implicitly[MyTypeclass[Int]].foo) // Prints "any"
println(implicitly[MyTypeclass[Boolean]].foo) // Prints "any"
println(implicitly[MyTypeclass[String]].foo) // Compilation error
你在最后一行得到的错误是:
<console>:25: error: ambiguous implicit values:
both method anyCanBeMyTC in object MyTypeclass of type [T]=> MyTypeclass[T]
and method specialForString in object MyTypeclass of type [T](implicit ev: <: <[T,String])MyTypeclass[T]
match expected type MyTypeclass[String]
println(implicitly[MyTypeclass[String]].foo)
这不会编译,因为隐式解析会发现歧义;在这种情况下,它有点人为,因为我们使用隐含证据定义 String
案例,以便在我们可以将其定义为 implicit def specialForString: MyTypeclass[String] = ...
并且没有任何歧义时触发歧义。但是在定义隐式实例和使用低优先级模式时,有些情况下需要依赖其他隐式参数,您可以按如下方式编写并使其正常工作:
trait MyTypeclass[T] { def foo: String }
trait LowPriorityInstances {
implicit def anyCanBeMyTC[T]: MyTypeclass[T] = new MyTypeclass[T] {
val foo = "any"
}
}
object MyTypeclass extends LowPriorityInstances {
implicit def specialForString[T](implicit ev: T <:< String): MyTypeclass[T] = new MyTypeclass[T] {
val foo = "string"
}
}
println(implicitly[MyTypeclass[Int]].foo) // Prints "any"
println(implicitly[MyTypeclass[Boolean]].foo) // Prints "any"
println(implicitly[MyTypeclass[String]].foo) // Prints "string"
还值得注意的是,此模式不限于两层,但您可以创建一个特征层次结构,并在其中包含隐式定义,这些定义从更具体的到更通用的继承树向上移动。
查看某些 Scala 库的源代码时,例如shapeless,我经常发现名为 LowPriorityImplicits
的特征。
你能解释一下这个模式吗?解决的问题是什么,pattern是怎么解决的?
该模式允许您拥有隐式层次结构,避免编译器出现与歧义相关的错误,并提供一种方法来确定它们的优先级。例如考虑以下内容:
trait MyTypeclass[T] { def foo: String }
object MyTypeclass {
implicit def anyCanBeMyTC[T]: MyTypeclass[T] = new MyTypeclass[T] {
val foo = "any"
}
implicit def specialForString[T](implicit ev: T <:< String): MyTypeclass[T] = new MyTypeclass[T] {
val foo = "string"
}
}
println(implicitly[MyTypeclass[Int]].foo) // Prints "any"
println(implicitly[MyTypeclass[Boolean]].foo) // Prints "any"
println(implicitly[MyTypeclass[String]].foo) // Compilation error
你在最后一行得到的错误是:
<console>:25: error: ambiguous implicit values:
both method anyCanBeMyTC in object MyTypeclass of type [T]=> MyTypeclass[T]
and method specialForString in object MyTypeclass of type [T](implicit ev: <: <[T,String])MyTypeclass[T]
match expected type MyTypeclass[String]
println(implicitly[MyTypeclass[String]].foo)
这不会编译,因为隐式解析会发现歧义;在这种情况下,它有点人为,因为我们使用隐含证据定义 String
案例,以便在我们可以将其定义为 implicit def specialForString: MyTypeclass[String] = ...
并且没有任何歧义时触发歧义。但是在定义隐式实例和使用低优先级模式时,有些情况下需要依赖其他隐式参数,您可以按如下方式编写并使其正常工作:
trait MyTypeclass[T] { def foo: String }
trait LowPriorityInstances {
implicit def anyCanBeMyTC[T]: MyTypeclass[T] = new MyTypeclass[T] {
val foo = "any"
}
}
object MyTypeclass extends LowPriorityInstances {
implicit def specialForString[T](implicit ev: T <:< String): MyTypeclass[T] = new MyTypeclass[T] {
val foo = "string"
}
}
println(implicitly[MyTypeclass[Int]].foo) // Prints "any"
println(implicitly[MyTypeclass[Boolean]].foo) // Prints "any"
println(implicitly[MyTypeclass[String]].foo) // Prints "string"
还值得注意的是,此模式不限于两层,但您可以创建一个特征层次结构,并在其中包含隐式定义,这些定义从更具体的到更通用的继承树向上移动。