解释Scala类型级编程中使用的"LowPriorityImplicits"模式 [英] Explain the `LowPriorityImplicits` pattern used in Scala type-level programming

查看:51
本文介绍了解释Scala类型级编程中使用的"LowPriorityImplicits"模式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

查看某些Scala库的来源时,例如 shapeless ,我经常会发现名为 LowPriorityImplicits 的特征.

When looking at the source of some Scala libraries, e.g. shapeless, I often find traits named LowPriorityImplicits.

您能解释一下这种模式吗?解决的问题是什么?模式如何解决?

Can you please explain this pattern? What is the problem that is solved, and how does the pattern solve it?

推荐答案

该模式允许您具有隐式层次结构,从而避免了编译器与歧义相关的错误,并提供了对它们进行优先级排序的方法.作为示例,请考虑以下内容:

That pattern allows you to have hierarchy of implicits avoiding ambiguity-related errors by the compiler and providing a way to prioritise them. As an example consider the following:

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

您在最后一行得到的错误是:

The error you get in the last line is:

<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]时触发歧义] = ... 并且没有任何歧义.但是在某些情况下,在定义隐式实例并使用低优先级模式时,您需要依赖于其他隐式参数,可以按如下所示编写它并使它正常工作:

This wouldn't compile because the implicit resolution will find ambiguity; in this case it is a bit artificial in that we are defining the String case using the implicit evidence in order to trigger the ambiguity when we could just define it as implicit def specialForString: MyTypeclass[String] = ... and not have any ambiguity. But there are cases where you need to depend on other implicit parameters when defining implicit instances and using the low-priority pattern you can write it as follows and have it work fine:

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"

还值得注意的是,这种模式不限于两层,而是可以创建特征的层次结构,并在它们中包含隐式定义,这些定义从更具体的到更通用的继承树.

It is also worth noting that this pattern is not limited to two layers but you can create a hierarchy of traits and have in them implicit definitions that go from more specific to more generic going up the inheritance tree.

这篇关于解释Scala类型级编程中使用的"LowPriorityImplicits"模式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆