Scala 中的类型类解析是如何工作的? [英] How does type class resolution in scala work?

查看:37
本文介绍了Scala 中的类型类解析是如何工作的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有类型参数的函数,我想确定该类型参数是否为 Option.我已经阅读了一些博客文章,即 这个,最近关于scala中的类型类,所以我想出了这个解决方案:

I have a function with a type parameter and I want to find out whether the type parameter is an Option or not. I have read some blogposts, i.e. this one, about type classes in scala recently, so I came up with this solution:

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]).显然,更具体的是选择的类型.但是我能保证编译器总是选择更具体的类型吗?无论如何,它在编译器中如何工作?我还没有找到有关此行为的文档.

In the case of Some(3) hitOption is the implicit parameter, even though notOption would match as well (with A = Option[Int]). Obviously the more specific is type chosen. But am I guaranteed that the compiler always chooses the more specific type? And how does that work in the compiler anyway? I did not find a documentation of this behavior yet.

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

Note: Maybe the title for this question is not best, I'll happily change it for a better one.

推荐答案

已经有一个关于这个的问题:Scala:隐式参数解析优先级.它通过复杂的博文来回答.我认为最重要的信息是 Martin Odersky 对博文的评论:

There is already a question about this: Scala: Implicit parameter resolution precedence. Which answers itself through a complicated blog post. I think the most important piece of information is in Martin Odersky's comment on the blog post:

这里有一个更高级的解释隐式搜索发生了什么在 Scala 中,这对应于规范如何解释它,但在稍微不那么形式化的语言.

Here's a more high-level explanation what goes on with implicit search in Scala, and which corresponds to how the spec explains it, but in slightly less formalistic language.

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

  1. First, we look for implicits that are visible either as locals or as members of enclosing classes and packages or as imports - the precise rule is that we should be able to access them using their name only, without any prefix.

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

If no implicits are found in step 1, we look in the "implicit scope", which contains all sort of companion objects that bear some relation to the type which we search for (i.e. companion object of the type itself, of its parameters if any are given, and also of its supertype and supertraits; the importance is to be as general as possible without reverting to whole program analysis like Haskell does).

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

If at either stage we find more than one implicit, disambiguation kicks in. Disambiguation is exactly the same as for overloading resolution. Static overloading resolution resolution rules are a bit involved, and I won't repeat them here. If it's any consolation: Java's rules and C#'s rules are considerably more complex than Scala's in this area.

现在根据这个解释,它是静态重载解析规则",它将消除 notOptionhitOption 之间的歧义.老实说,我不明白怎么做.

Now according to this explanation it are "the rules of static overloading resolution" which will disambiguate between notOption and hitOption. To be honest, I fail to see how.

这个答案 解释了确实具有更具体参数的方法具有优先权,但我不知道是否或如何与重载规则有关.

This answer explains that indeed methods with more specific arguments have priority, but I don't know if or how that is related to the overloading rules.

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

If I were you I would not depend on this behavior too much, but use the easier to understand concept of implicit priority through inheritance. It's a good idea to put your implicits in the companion object anyway.

归结为这样一个事实,即被继承的隐式具有较低的优先级.因此,如果 hitOption 与伴随对象扩展的特征不匹配,则可以安全地将您退回到隐式.

It boils down to the fact that implicits that are inherited have lower priority. So it's safe to put the implicit you fall back to if hitOption doesn't match in a trait that the companion object extends.

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._ 导入它们,这也应该有效.

This should also work if you put your implicits in a non companion object MyImplicits and import them with import MyImplicits._.

这篇关于Scala 中的类型类解析是如何工作的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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