Scala 在哪里寻找隐式?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5598085/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Where does Scala look for implicits?
提问by Daniel C. Sobral
An implicitquestion to newcomers to Scala seems to be: where does the compiler look for implicits? I mean implicit because the question never seems to get fully formed, as if there weren't words for it. :-) For example, where do the values for integralbelow come from?
对于 Scala 新手来说,一个隐含的问题似乎是:编译器在哪里寻找隐式?我的意思是隐含的,因为这个问题似乎从来没有完全形成,好像没有词可以形容。:-) 例如,integral下面的值从何而来?
scala> import scala.math._
import scala.math._
scala> def foo[T](t: T)(implicit integral: Integral[T]) {println(integral)}
foo: [T](t: T)(implicit integral: scala.math.Integral[T])Unit
scala> foo(0)
scala.math.Numeric$IntIsIntegral$@3dbea611
scala> foo(0L)
scala.math.Numeric$LongIsIntegral$@48c610af
Another question that does follow up to those who decide to learn the answer to the first question is how does the compiler choose which implicit to use, in certain situations of apparent ambiguity (but that compile anyway)?
对于决定学习第一个问题的答案的人来说,另一个问题是,在某些明显不明确的情况下(但无论如何编译),编译器如何选择使用哪个隐式?
For instance, scala.Predefdefines two conversions from String: one to WrappedStringand another to StringOps. Both classes, however, share a lot of methods, so why doesn't Scala complain about ambiguity when, say, calling map?
例如,scala.Predef定义了两个从 的转换String:一个到WrappedString和另一个到StringOps。然而,这两个类共享很多方法,那么为什么 Scala 在调用 时不抱怨歧义map呢?
Note:this question was inspired by this other question, in the hopes of stating the problem in a more general manner. The example was copied from there, because it is referred to in the answer.
注意:这个问题的灵感来自另一个问题,希望以更一般的方式说明问题。该示例是从那里复制的,因为答案中提到了它。
回答by Daniel C. Sobral
Types of Implicits
隐式类型
Implicits in Scala refers to either a value that can be passed "automatically", so to speak, or a conversion from one type to another that is made automatically.
Scala 中的隐式指的是可以“自动”传递的值,可以这么说,或者是自动进行从一种类型到另一种类型的转换。
Implicit Conversion
隐式转换
Speaking very briefly about the latter type, if one calls a method mon an object oof a class C, and that class does not support method m, then Scala will look for an implicit conversion from Cto something that doessupport m. A simple example would be the method mapon String:
说到非常简单说一下后一种类型,如果调用一个方法m的对象上o的一类C,而该类不是不支持的方法m,然后将斯卡拉寻找从隐式转换C的东西,不支持m。一个简单的例子是方法map上String:
"abc".map(_.toInt)
Stringdoes not support the method map, but StringOpsdoes, and there's an implicit conversion from Stringto StringOpsavailable (see implicit def augmentStringon Predef).
String不支持的方法map,但StringOps确实,而且也从一个隐式转换String到StringOps可用的(见implicit def augmentString上Predef)。
Implicit Parameters
隐式参数
The other kind of implicit is the implicit parameter. These are passed to method calls like any other parameter, but the compiler tries to fill them in automatically. If it can't, it will complain. One canpass these parameters explicitly, which is how one uses breakOut, for example (see question about breakOut, on a day you are feeling up for a challenge).
另一种隐式是隐式参数。它们像任何其他参数一样传递给方法调用,但编译器会尝试自动填充它们。如果不能,它会抱怨。一个可以明确地传递这些参数,这是怎么一个用途breakOut,例如(查看有关的问题breakOut,某一天你感到了一个挑战)。
In this case, one has to declare the need for an implicit, such as the foomethod declaration:
在这种情况下,必须声明需要隐式,例如foo方法声明:
def foo[T](t: T)(implicit integral: Integral[T]) {println(integral)}
View Bounds
查看边界
There's one situation where an implicit is both an implicit conversion and an implicit parameter. For example:
在一种情况下,隐式既是隐式转换又是隐式参数。例如:
def getIndex[T, CC](seq: CC, value: T)(implicit conv: CC => Seq[T]) = seq.indexOf(value)
getIndex("abc", 'a')
The method getIndexcan receive any object, as long as there is an implicit conversion available from its class to Seq[T]. Because of that, I can pass a Stringto getIndex, and it will work.
该方法getIndex可以接收任何对象,只要存在从其类到Seq[T]. 正因为如此,我可以传递一个Stringto getIndex,它会起作用。
Behind the scenes, the compiler changes seq.IndexOf(value)to conv(seq).indexOf(value).
在幕后,编译器更改seq.IndexOf(value)为conv(seq).indexOf(value).
This is so useful that there is syntactic sugar to write them. Using this syntactic sugar, getIndexcan be defined like this:
这非常有用,以至于需要使用语法糖来编写它们。使用这个语法糖,getIndex可以这样定义:
def getIndex[T, CC <% Seq[T]](seq: CC, value: T) = seq.indexOf(value)
This syntactic sugar is described as a view bound, akin to an upper bound(CC <: Seq[Int]) or a lower bound(T >: Null).
这种语法糖被描述为视图边界,类似于上限( CC <: Seq[Int]) 或下限( T >: Null)。
Context Bounds
上下文边界
Another common pattern in implicit parameters is the type class pattern. This pattern enables the provision of common interfaces to classes which did not declare them. It can both serve as a bridge pattern -- gaining separation of concerns -- and as an adapter pattern.
隐式参数中的另一个常见模式是类型类模式。这种模式可以为没有声明它们的类提供公共接口。它既可以用作桥接模式(获得关注点分离),也可以用作适配器模式。
The Integralclass you mentioned is a classic example of type class pattern. Another example on Scala's standard library is Ordering. There's a library that makes heavy use of this pattern, called Scalaz.
Integral您提到的类是类型类模式的经典示例。Scala 标准库的另一个例子是Ordering. 有一个库大量使用了这种模式,称为 Scalaz。
This is an example of its use:
这是它的使用示例:
def sum[T](list: List[T])(implicit integral: Integral[T]): T = {
import integral._ // get the implicits in question into scope
list.foldLeft(integral.zero)(_ + _)
}
There is also syntactic sugar for it, called a context bound, which is made less useful by the need to refer to the implicit. A straight conversion of that method looks like this:
它也有语法糖,称为上下文绑定,由于需要引用隐式而变得不太有用。该方法的直接转换如下所示:
def sum[T : Integral](list: List[T]): T = {
val integral = implicitly[Integral[T]]
import integral._ // get the implicits in question into scope
list.foldLeft(integral.zero)(_ + _)
}
Context bounds are more useful when you just need to passthem to other methods that use them. For example, the method sortedon Seqneeds an implicit Ordering. To create a method reverseSort, one could write:
当您只需要将它们传递给使用它们的其他方法时,上下文边界更有用。例如,方法sortedonSeq需要一个隐式的Ordering. 要创建一个方法reverseSort,可以这样写:
def reverseSort[T : Ordering](seq: Seq[T]) = seq.sorted.reverse
Because Ordering[T]was implicitly passed to reverseSort, it can then pass it implicitly to sorted.
因为Ordering[T]被隐式传递给reverseSort,所以它可以隐式传递给sorted。
Where do Implicits come from?
隐式来自哪里?
When the compiler sees the need for an implicit, either because you are calling a method which does not exist on the object's class, or because you are calling a method that requires an implicit parameter, it will search for an implicit that will fit the need.
当编译器发现需要隐式参数时,要么是因为您正在调用对象的类中不存在的方法,要么是因为您正在调用需要隐式参数的方法,它将搜索适合需要的隐式参数.
This search obey certain rules that define which implicits are visible and which are not. The following table showing where the compiler will search for implicits was taken from an excellent presentationabout implicits by Josh Suereth, which I heartily recommend to anyone wanting to improve their Scala knowledge. It has been complemented since then with feedback and updates.
此搜索遵循某些规则,这些规则定义了哪些隐式可见,哪些不可见。下表显示了编译器将在何处搜索隐式,摘自Josh Suereth 关于隐式的精彩演示,我衷心推荐给任何想要提高他们的 Scala 知识的人。从那时起,它得到了反馈和更新的补充。
The implicits available under number 1 below has precedence over the ones under number 2. Other than that, if there are several eligible arguments which match the implicit parameter's type, a most specific one will be chosen using the rules of static overloading resolution (see Scala Specification §6.26.3). More detailed information can be found in a question I link to at the end of this answer.
下面数字 1 下可用的隐式优先于数字 2 下的那些。除此之外,如果有几个符合条件的参数匹配隐式参数的类型,将使用静态重载解析规则选择最具体的一个(参见 Scala规范 §6.26.3)。更详细的信息可以在我链接到本答案末尾的问题中找到。
- First look in current scope
- Implicits defined in current scope
- Explicit imports
- wildcard imports
Same scope in other files
- Now look at associated types in
- Companion objects of a type
- Implicit scope of an argument's type (2.9.1)
- Implicit scope of type arguments (2.8.0)
- Outer objects for nested types
- Other dimensions
- 首先查看当前范围
- 当前作用域中定义的隐式
- 显式导入
- 通配符导入
其他文件中的相同范围
- 现在看看相关的类型
- 类型的伴随对象
- 参数类型的隐式作用域(2.9.1)
- 类型参数的隐式作用域(2.8.0)
- 嵌套类型的外部对象
- 其他尺寸
Let's give some examples for them:
让我们为他们举一些例子:
Implicits Defined in Current Scope
当前作用域中定义的隐式
implicit val n: Int = 5
def add(x: Int)(implicit y: Int) = x + y
add(5) // takes n from the current scope
Explicit Imports
显式导入
import scala.collection.JavaConversions.mapAsScalaMap
def env = System.getenv() // Java map
val term = env("TERM") // implicit conversion from Java Map to Scala Map
Wildcard Imports
通配符导入
def sum[T : Integral](list: List[T]): T = {
val integral = implicitly[Integral[T]]
import integral._ // get the implicits in question into scope
list.foldLeft(integral.zero)(_ + _)
}
Same Scope in Other Files
其他文件中的相同范围
Edit: It seems this does not have a different precedence. If you have some example that demonstrates a precedence distinction, please make a comment. Otherwise, don't rely on this one.
编辑:这似乎没有不同的优先级。如果您有一些示例可以证明优先级区别,请发表评论。否则,不要依赖这个。
This is like the first example, but assuming the implicit definition is in a different file than its usage. See also how package objectsmight be used in to bring in implicits.
这与第一个示例类似,但假设隐式定义与其用法位于不同的文件中。另请参阅如何使用包对象引入隐式。
Companion Objects of a Type
一种类型的伴随对象
There are two object companions of note here. First, the object companion of the "source" type is looked into. For instance, inside the object Optionthere is an implicit conversion to Iterable, so one can call Iterablemethods on Option, or pass Optionto something expecting an Iterable. For example:
这里有两个值得注意的对象伴侣。首先,查看“源”类型的对象伴侣。例如,在对象内部Option有一个到 的隐式转换Iterable,因此可以调用 上的Iterable方法Option,或传递Option给期望为 的东西Iterable。例如:
for {
x <- List(1, 2, 3)
y <- Some('x')
} yield (x, y)
That expression is translated by the compiler to
该表达式由编译器翻译为
List(1, 2, 3).flatMap(x => Some('x').map(y => (x, y)))
However, List.flatMapexpects a TraversableOnce, which Optionis not. The compiler then looks inside Option's object companion and finds the conversion to Iterable, which is a TraversableOnce, making this expression correct.
但是,List.flatMap期望 a TraversableOnce,这Option不是。然后编译器查看Option的对象伴生内部并找到转换为Iterable,即 a TraversableOnce,从而使该表达式正确。
Second, the companion object of the expected type:
二、期望类型的伴生对象:
List(1, 2, 3).sorted
The method sortedtakes an implicit Ordering. In this case, it looks inside the object Ordering, companion to the class Ordering, and finds an implicit Ordering[Int]there.
该方法sorted采用隐式Ordering. 在这种情况下,它查看对象内部Ordering,类的伴侣Ordering,并在Ordering[Int]那里找到一个隐式。
Note that companion objects of super classes are also looked into. For example:
请注意,还研究了超类的伴生对象。例如:
class A(val n: Int)
object A {
implicit def str(a: A) = "A: %d" format a.n
}
class B(val x: Int, y: Int) extends A(y)
val b = new B(5, 2)
val s: String = b // s == "A: 2"
This is how Scala found the implicit Numeric[Int]and Numeric[Long]in your question, by the way, as they are found inside Numeric, not Integral.
这是斯卡拉如何发现隐含的Numeric[Int],并Numeric[Long]在你的问题,顺便说一下,因为他们发现里面Numeric不是Integral。
Implicit Scope of an Argument's Type
参数类型的隐式作用域
If you have a method with an argument type A, then the implicit scope of type Awill also be considered. By "implicit scope" I mean that all these rules will be applied recursively -- for example, the companion object of Awill be searched for implicits, as per the rule above.
如果您有一个带有参数 type 的方法A,那么A也会考虑type 的隐式作用域。通过“隐式范围”,我的意思是所有这些规则都将被递归地应用——例如,A按照上述规则,将搜索的伴生对象以查找隐式。
Note that this does not mean the implicit scope of Awill be searched for conversions of that parameter, but of the whole expression. For example:
请注意,这并不意味着A将搜索的隐式范围以查找该参数的转换,而是搜索整个表达式的转换。例如:
class A(val n: Int) {
def +(other: A) = new A(n + other.n)
}
object A {
implicit def fromInt(n: Int) = new A(n)
}
// This becomes possible:
1 + new A(1)
// because it is converted into this:
A.fromInt(1) + new A(1)
This is available since Scala 2.9.1.
这从 Scala 2.9.1 开始可用。
Implicit Scope of Type Arguments
类型参数的隐式作用域
This is required to make the type class pattern really work. Consider Ordering, for instance: It comes with some implicits in its companion object, but you can't add stuff to it. So how can you make an Orderingfor your own class that is automatically found?
这是使类型类模式真正起作用所必需的。考虑一下Ordering,例如:它在它的伴生对象中带有一些隐式,但你不能向它添加东西。那么如何Ordering为自己的类创建一个自动找到的类呢?
Let's start with the implementation:
让我们从实现开始:
class A(val n: Int)
object A {
implicit val ord = new Ordering[A] {
def compare(x: A, y: A) = implicitly[Ordering[Int]].compare(x.n, y.n)
}
}
So, consider what happens when you call
所以,考虑一下当你打电话时会发生什么
List(new A(5), new A(2)).sorted
As we saw, the method sortedexpects an Ordering[A](actually, it expects an Ordering[B], where B >: A). There isn't any such thing inside Ordering, and there is no "source" type on which to look. Obviously, it is finding it inside A, which is a type argumentof Ordering.
正如我们所见,该方法sorted需要一个Ordering[A](实际上,它需要一个Ordering[B], where B >: A)。里面没有任何这样的东西Ordering,也没有可以查看的“源”类型。显然,这是里面找到它A,这是一个类型参数的Ordering。
This is also how various collection methods expecting CanBuildFromwork: the implicits are found inside companion objects to the type parameters of CanBuildFrom.
这也是各种期望CanBuildFrom工作的集合方法的工作方式:隐式在伴生对象中找到CanBuildFrom.
Note: Orderingis defined as trait Ordering[T], where Tis a type parameter. Previously, I said that Scala looked inside type parameters, which doesn't make much sense. The implicit looked for above is Ordering[A], where Ais an actual type, not type parameter: it is a type argumentto Ordering. See section 7.2 of the Scala specification.
注意:Ordering定义为trait Ordering[T],其中T是类型参数。之前说Scala在类型参数里面看,没多大意义。看了上述隐式是Ordering[A],在这里A是一个实际的类型,而不是输入参数:它是一个类型参数来Ordering。请参阅 Scala 规范的第 7.2 节。
This is available since Scala 2.8.0.
这从 Scala 2.8.0 开始可用。
Outer Objects for Nested Types
嵌套类型的外部对象
I haven't actually seen examples of this. I'd be grateful if someone could share one. The principle is simple:
我实际上还没有看到这样的例子。如果有人可以分享一个,我将不胜感激。原理很简单:
class A(val n: Int) {
class B(val m: Int) { require(m < n) }
}
object A {
implicit def bToString(b: A#B) = "B: %d" format b.m
}
val a = new A(5)
val b = new a.B(3)
val s: String = b // s == "B: 3"
Other Dimensions
其他尺寸
I'm pretty sure this was a joke, but this answer might not be up-to-date. So don't take this question as being the final arbiter of what is happening, and if you do noticed it has gotten out-of-date, please inform me so that I can fix it.
我很确定这是一个笑话,但这个答案可能不是最新的。因此,不要将此问题视为正在发生的事情的最终仲裁者,如果您确实注意到它已经过时,请通知我,以便我可以修复它。
EDIT
编辑
Related questions of interest:
感兴趣的相关问题:
回答by Eugene Yokota
I wanted to find out the precedence of the implicit parameter resolution, not just where it looks for, so I wrote a blog post revisiting implicits without import tax(and implicit parameter precedence againafter some feedback).
我想找出隐式参数解析的优先级,而不仅仅是它查找的位置,所以我写了一篇博客文章重新审视了没有进口税的隐式(以及在一些反馈后再次使用隐式参数优先级)。
Here's the list:
这是清单:
- 1) implicits visible to current invocation scope via local declaration, imports, outer scope, inheritance, package object that are accessible without prefix.
- 2) implicit scope, which contains all sort of companion objects and package object that bear some relation to the implicit's type which we search for (i.e. package object of the type, companion object of the type itself, of its type constructor if any, of its parameters if any, and also of its supertype and supertraits).
- 1) 通过局部声明、导入、外部作用域、继承、包对象隐式对当前调用范围可见,这些对象无需前缀即可访问。
- 2)隐式范围,它包含所有类型的伴生对象和包对象,这些对象与我们搜索的隐式类型有某种关系(即类型的包对象、类型本身的伴生对象、其类型构造函数(如果有的话)、它的参数(如果有的话),以及它的超类型和超特征)。
If at either stage we find more than one implicit, static overloading rule is used to resolve it.
如果在任一阶段我们发现多个隐式的静态重载规则用于解决它。

