【问题标题】:Explain the `LowPriorityImplicits` pattern used in Scala type-level programming解释 Scala 类型级编程中使用的 `LowPriorityImplicits` 模式
【发布时间】:2016-02-06 06:24:49
【问题描述】:

查看一些 Scala 库的源代码时,例如shapeless,我经常发现名为 LowPriorityImplicits 的特征。

你能解释一下这个模式吗?解决了什么问题,模式是如何解决的?

【问题讨论】:

    标签: scala implicit shapeless type-level-computation


    【解决方案1】:

    该模式允许您拥有隐式层次结构,避免编译器产生与歧义相关的错误,并提供一种方法来确定它们的优先级。例如,考虑以下内容:

    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"
    

    还值得注意的是,这种模式不限于两层,您可以创建一个特征层次结构,并在其中包含从更具体到更通用的隐含定义。

    【讨论】:

    • 这是否适用于您无法控制的类型类或正在修改所需的伴随对象?
    • 我不完全确定我是否必须诚实,但您可以通过将实例放在通用 object(不是伴侣)中进行实验,让它扩展低优先级特征然后显式导入对象。
    猜你喜欢
    • 2015-10-30
    • 1970-01-01
    • 2018-11-26
    • 1970-01-01
    • 2015-06-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多