【问题标题】:How does type class resolution in scala work?scala 中的类型类解析如何工作?
【发布时间】:2017-01-27 15:08:58
【问题描述】:

我有一个带有类型参数的函数,我想知道类型参数是否为Option。我最近阅读了一些关于 scala 中类型类的博文,即this one,所以我想出了这个解决方案:

case class OptionFinder[A](isOption: Boolean)
implicit def notOption[A]: OptionFinder[A] = OptionFinder(false)
implicit def hitOption[A]: OptionFinder[Option[A]] = OptionFinder(true)

def myFunction[A](value: A)(implicit optionFinder: OptionFinder[A]): String = {
    if (optionFinder.isOption) {"Found Option!"} else {"Found something else."}
}

这似乎按预期工作:

scala> val x: Option[Int] = Some(3)
scala> myFunction(x)
res0: String = Found Option!

scala> val y: String = "abc"
scala> myFunction(y)
res1: String = Found something else.

Some(3) 的情况下,hitOption 是隐式参数,即使notOption 也会匹配(A = Option[Int])。显然,更具体的是选择的类型。但是我能保证编译器总是选择更具体的类型吗?无论如何,它在编译器中是如何工作的?我还没有找到有关此行为的文档。

注意:也许这个问题的标题不是最好的,我很乐意将它改成更好的。

【问题讨论】:

  • 都在隐式解析中,说不定stackoverflow.com/questions/5598085/…有东西。
  • 是的,但是这里两个隐式都在同一个(=当前)范围内。我的问题是:编译器如何在两者之间进行选择?

标签: scala


【解决方案1】:

已经有一个关于这个的问题:Scala: Implicit parameter resolution precedence。它通过一个复杂的blog post 来回答自己。我认为最重要的信息是 Martin Odersky 对博文的评论:

这是一个更高级的解释,隐式搜索发生了什么 在 Scala 中,这与规范的解释方式相对应,但在 稍微不那么形式化的语言。

  1. 首先,我们寻找作为局部变量或作为封闭类和包的成员或作为导入可见的隐式 - 准确的规则是我们应该能够使用他们的名字访问他们 只有,没有任何前缀。

  2. 如果在第 1 步中没有找到隐式,我们查看“隐式范围”,其中包含所有类型的伴随对象 与我们搜索的类型的关系(即 类型本身,它的参数(如果有的话),以及它的 超类型和超特征;重要的是要像一般 无需像 Haskell 那样恢复到整个程序分析即可 确实)。

如果在任一阶段我们发现不止一个隐含的消歧 开始。消歧与重载完全相同 解析度。静态重载解析解析规则有点 涉及,这里不再赘述。如果有什么安慰的话: Java 的规则和 C# 的规则比 Scala 的规则复杂得多 在这个区域。

现在根据这个解释,它是“静态重载解决规则”,它将消除notOptionhitOption 之间的歧义。老实说,我看不出怎么做。

This answer 解释说确实具有更具体参数的方法具有优先权,但我不知道这是否或如何与重载规则相关。

如果我是你,我不会过多地依赖这种行为,而是通过继承使用更容易理解的隐式优先级概念。无论如何,将你的隐式放在伴生对象中是个好主意。

归结为继承的隐式具有较低优先级的事实。因此,如果 hitOption 与伴生对象扩展的特征不匹配,则可以安全地回退隐式。

case class OptionFinder[A](isOption: Boolean)

object OptionFinder extends LowerPriority {
  implicit def hitOption[A]: OptionFinder[Option[A]] = OptionFinder(true)
}

trait LowerPriority {
  implicit def notOption[A]: OptionFinder[A] = OptionFinder(false)
}

def myFunction[A](value: A)(implicit optionFinder: OptionFinder[A]): String = {
    if (optionFinder.isOption) {"Found Option!"} else {"Found something else."}
}

如果你将你的隐式放在一个非伴随对象 MyImplicits 中并使用 import MyImplicits._ 导入它们,这也应该有效。

【讨论】:

  • 谢谢,所以消除歧义是我想寻找的词。规范中关于overloading resolution 的章节有一些“具体为”和“比更具体”的定义,但我需要一些时间来消化它。同时,我喜欢您的替代解决方案,它可以保证有效。
猜你喜欢
  • 2020-08-05
  • 1970-01-01
  • 1970-01-01
  • 2021-06-14
  • 1970-01-01
  • 2021-11-17
  • 1970-01-01
  • 2014-06-30
  • 1970-01-01
相关资源
最近更新 更多