将 scala.math.Integral 作为隐式参数传递
我已阅读关于 scala.math.Integral 但我不明白 Integral[T] 时会发生什么
传递为隐式参数。 (我想我总体上理解隐式参数的概念)。
让我们考虑这个函数
import scala.math._
def foo[T](t: T)(implicit integral: Integral[T]) { println(integral) }
现在我在 REPL 中调用 foo
:
scala> foo(0)
scala.math.Numeric$IntIsIntegral$@581ea2
scala> foo(0L)
scala.math.Numeric$LongIsIntegral$@17fe89
integral
参数如何变成 scala.math.Numeric$IntIsIntegral
和 scala .math.Numeric$LongIsIntegral
?
I have read the answer to my question about scala.math.Integral but I do not understand what happens when Integral[T]
is passed as an implicit parameter. (I think I understand the implicit parameters concept in general).
Let's consider this function
import scala.math._
def foo[T](t: T)(implicit integral: Integral[T]) { println(integral) }
Now I call foo
in REPL:
scala> foo(0)
scala.math.Numeric$IntIsIntegral$@581ea2
scala> foo(0L)
scala.math.Numeric$LongIsIntegral$@17fe89
How does the integral
argument become scala.math.Numeric$IntIsIntegral
and scala.math.Numeric$LongIsIntegral
?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
简而言之,Scala 在对象
Numeric
中找到IntIsIntegral
和LongIsIntegral
,该对象是类Numeric
的伴生对象code>,它是 Integral 的超类。继续阅读长答案。
隐式类型
Scala 中的隐式指的是可以“自动”传递的值,或者是自动进行的从一种类型到另一种类型的转换。
隐式转换
非常简单地谈论后一种类型,如果有人在类
C
的对象o
上调用方法m
,并且该类执行以下操作:不支持方法m
,那么 Scala 将寻找从C
到支持支持m
的隐式转换。一个简单的例子是String
上的map
方法:String
不支持map
方法,但是>StringOps
确实如此,并且可以从String
到StringOps
进行隐式转换(请参阅Predef< 上的
implicit def AugmentString
) /代码>)。隐式参数
另一种隐式参数是隐式参数。这些参数像任何其他参数一样传递给方法调用,但编译器会尝试自动填充它们。如果做不到,就会抱怨。 可以显式传递这些参数,例如,这就是使用
breakOut
的方式(请参阅有关breakOut
的问题,在您心情不好的一天来挑战)。在这种情况下,必须声明对隐式的需要,例如
foo
方法声明:View Bounds
有一种情况,隐式既是隐式转换又是隐式参数。例如:
方法
getIndex
可以接收任何对象,只要存在从其类到Seq[T]
的隐式转换。因此,我可以将String
传递给getIndex
,并且它会起作用。在幕后,编译将
seq.IndexOf(value)
更改为conv(seq).indexOf(value)
。这非常有用,以至于有一个语法糖可以编写它们。使用此语法糖,
getIndex
可以这样定义:此语法糖被描述为视图边界,类似于上限(< code>CC <: Seq[Int])或下限(
T >: Null
)。请注意,视图边界从 2.11 开始已弃用,您应该避免使用它们。
上下文边界
隐式参数中的另一个常见模式是类型类模式。此模式可以为未声明公共接口的类提供公共接口。它既可以充当桥梁模式(实现关注点分离),也可以充当适配器模式。
您提到的 Integral 类是类型类模式的经典示例。 Scala 标准库的另一个例子是
Ordering
。有一个库大量使用了这种模式,称为 Scalaz。这是它的使用示例:
它还有一个语法糖,称为上下文绑定,由于需要引用隐式内容,因此它的用处不大。该方法的直接转换如下所示:
当您只需将上下文边界传递给使用它们的其他方法时,上下文边界会更有用。例如,
Seq
上的sorted
方法需要隐式Ordering
。要创建方法reverseSort
,可以这样写:因为
Ordering[T]
已隐式传递给reverseSort
,因此它可以将其隐式传递给 <代码>排序。隐式从何而来?
当编译器发现需要隐式参数时,无论是因为您正在调用对象的类中不存在的方法,还是因为您正在调用需要隐式参数的方法,它将搜索适合需要的隐式参数。
此搜索遵循某些规则,这些规则定义了哪些隐式可见,哪些不可见。下表显示了编译器将在何处搜索隐式内容,取自出色的 Josh Suereth 的关于隐式的演示,我衷心推荐给任何想要提高 Scala 知识的人。
让我们举例说明。
当前范围中定义的隐式
显式导入
通配符导入
其他文件中的相同范围
这类似于第一个示例,但假设隐式定义与其用法位于不同的文件中。另请参阅如何使用包对象来引入隐式。
类型的伴生对象
这里有两个值得注意的伴生对象。首先,研究“源”类型的对象伴生体。例如,在对象
Option
内部有一个到Iterable
的隐式转换,因此可以在Option
上调用Iterable
方法>,或将Option
传递给需要Iterable
的对象。例如:该表达式由编译转换为
然而,
List.flatMap
需要一个TraversableOnce
,而Option
则不是。然后,编译器查看Option
的对象伴生对象并找到到Iterable
的转换,即TraversableOnce
,从而使该表达式正确。其次,预期类型的伴生对象:
sorted
方法采用隐式Ordering
。在本例中,它会查找与Ordering
类相伴的对象Ordering
内部,并在那里找到隐式Ordering[Int]
。请注意,还会研究超类的伴生对象。例如:顺便说一句,
这就是 Scala 在您的问题中找到隐式
Numeric[Int]
和Numeric[Long]
的方式,因为它们是在Numeric 中找到的
,而不是整体
。类型参数的伴随对象 类型
这是使类型类模式真正发挥作用所必需的。例如,考虑
Ordering
...它的伴生对象中带有一些隐式内容,但您无法向其中添加内容。那么如何为您自己的类创建一个自动找到的Ordering
呢?让我们从实现开始:
因此,考虑调用时会发生什么
正如我们所见,方法
sorted
需要一个Ordering[A]
(实际上,它需要一个排序[B]
,其中B >:A
)。Ordering
中没有任何这样的东西,并且没有可供查看的“源”类型。显然,它是在A
中找到的,它是Ordering
的类型参数。这也是各种需要
CanBuildFrom
的集合方法的工作方式:在CanBuildFrom
的类型参数的伴生对象中找到隐式。嵌套类型的外部对象
我实际上还没有看到这样的例子。如果有人可以分享一个,我将不胜感激。原理很简单:
其他维度
我很确定这是一个笑话。我希望。 :-)
编辑
感兴趣的相关问题:
The short answer is that Scala finds
IntIsIntegral
andLongIsIntegral
inside the objectNumeric
, which is the companion object of the classNumeric
, which is a super class ofIntegral
.Read on for the long answer.
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.
Implicit Conversion
Speaking very briefly about the latter type, if one calls a method
m
on an objecto
of a classC
, and that class does not support methodm
, then Scala will look for an implicit conversion fromC
to something that does supportm
. A simple example would be the methodmap
onString
:String
does not support the methodmap
, butStringOps
does, and there's an implicit conversion fromString
toStringOps
available (seeimplicit def augmentString
onPredef
).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 can pass these parameters explicitly, which is how one uses
breakOut
, for example (see question aboutbreakOut
, on a day you are feeling up for a challenge).In this case, one has to declare the need for an implicit, such as the
foo
method declaration:View Bounds
There's one situation where an implicit is both an implicit conversion and an implicit parameter. For example:
The method
getIndex
can receive any object, as long as there is an implicit conversion available from its class toSeq[T]
. Because of that, I can pass aString
togetIndex
, and it will work.Behind the scenes, the compile changes
seq.IndexOf(value)
toconv(seq).indexOf(value)
.This is so useful that there is a syntactic sugar to write them. Using this syntactic sugar,
getIndex
can be defined like this:This syntactic sugar is described as a view bound, akin to an upper bound (
CC <: Seq[Int]
) or a lower bound (T >: Null
).Please be aware that view bounds are deprecated from 2.11, you should avoid them.
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
Integral
class you mentioned is a classic example of type class pattern. Another example on Scala's standard library isOrdering
. There's a library that makes heavy use of this pattern, called Scalaz.This is an example of its use:
There is also a 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:
Context bounds are more useful when you just need to pass them to other methods that use them. For example, the method
sorted
onSeq
needs an implicitOrdering
. To create a methodreverseSort
, one could write:Because
Ordering[T]
was implicitly passed toreverseSort
, it can then pass it implicitly tosorted
.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 presentation about implicits by Josh Suereth, which I heartily recommend to anyone wanting to improve their Scala knowledge.
Let's give examples for them.
Implicits Defined in Current Scope
Explicit Imports
Wildcard Imports
Same Scope in Other Files
This is like the first example, but assuming the implicit definition is in a different file than its usage. See also how package objects might 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
Option
there is an implicit conversion toIterable
, so one can callIterable
methods onOption
, or passOption
to something expecting anIterable
. For example:That expression is translated by the compile into
However,
List.flatMap
expects aTraversableOnce
, whichOption
is not. The compiler then looks insideOption
's object companion and finds the conversion toIterable
, which is aTraversableOnce
, making this expression correct.Second, the companion object of the expected type:
The method
sorted
takes an implicitOrdering
. In this case, it looks inside the objectOrdering
, companion to the classOrdering
, and finds an implicitOrdering[Int]
there.Note that companion objects of super classes are also looked into. For example:
This is how Scala found the implicit
Numeric[Int]
andNumeric[Long]
in your question, by the way, as they are found insideNumeric
, notIntegral
.Companion Objects of Type Parameters Types
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 anOrdering
for your own class that is automatically found?Let's start with the implementation:
So, consider what happens when you call
As we saw, the method
sorted
expects anOrdering[A]
(actually, it expects anOrdering[B]
, whereB >: A
). There isn't any such thing insideOrdering
, and there is no "source" type on which to look. Obviously, it is finding it insideA
, which is a type parameter ofOrdering
.This is also how various collection methods expecting
CanBuildFrom
work: the implicits are found inside companion objects to the type parameters ofCanBuildFrom
.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:
Other Dimensions
I'm pretty sure this was a joke. I hope. :-)
EDIT
Related questions of interest:
该参数是隐式的,这意味着 Scala 编译器将查找是否可以在某个地方找到可以自动填充该参数的隐式对象。
当您传入
Int
时,它将查找一个Integral[Int]
隐式对象,并在scala.math.Numeric< 中找到它/代码>。您可以查看 scala.math.Numeric 的源代码,您会发现:
同样,Long 有一个不同的隐式对象,其工作方式相同。
The parameter is
implicit
, which means that the Scala compiler will look if it can find an implicit object somewhere that it can automatically fill in for the parameter.When you pass in an
Int
, it's going to look for an implicit object that is anIntegral[Int]
and it finds it inscala.math.Numeric
. You can look at the source code ofscala.math.Numeric
, where you will find this:Likewise, there is a different implicit object for
Long
that works the same way.