我已经阅读了关于scala.math.Integral的问题的答案但是我不明白当作为隐式参数传递时会发生什么.(我想我一般都理解隐式参数概念).Integral[T]
让我们考虑一下这个功能
import scala.math._
def foo[T](t: T)(implicit integral: Integral[T]) { println(integral) }
Run Code Online (Sandbox Code Playgroud)
现在我打电话给fooREPL:
scala> foo(0)
scala.math.Numeric$IntIsIntegral$@581ea2
scala> foo(0L)
scala.math.Numeric$LongIsIntegral$@17fe89Run Code Online (Sandbox Code Playgroud)
怎样的integral争论变得scala.math.Numeric$IntIsIntegral和scala.math.Numeric$LongIsIntegral?
Dan*_*ral 28
简短的回答是,斯卡拉发现IntIsIntegral和LongIsIntegral对象内部Numeric,它是类的同伴对象Numeric,这是一个超类的Integral.
请继续阅读以获得详尽的答案.
Scala中的Implicits指的是可以"自动"传递的值,可以这么说,或者是自动转换为从一种类型到另一种类型的转换.
说到非常简单说一下后一种类型,如果调用一个方法m的对象上o的一类C,而这个类不支持的方法m,然后将斯卡拉寻找从隐式转换C的东西,不支持m.一个简单的例子是方法map上String:
"abc".map(_.toInt)
Run Code Online (Sandbox Code Playgroud)
String不支持该方法map,但StringOps确实如此,并且隐式转换String为StringOps可用(请参阅implicit def augmentString参考资料Predef).
另一种隐含的是隐式参数.它们像任何其他参数一样传递给方法调用,但编译器会尝试自动填充它们.如果不能,它会抱怨.人们可以明确地传递这些参数,breakOut例如(例如breakOut,在你感觉挑战的那一天,请参阅问题).
在这种情况下,必须声明需要隐式,例如foo方法声明:
def foo[T](t: T)(implicit integral: Integral[T]) {println(integral)}
Run Code Online (Sandbox Code Playgroud)
有一种情况,隐式是隐式转换和隐式参数.例如:
def getIndex[T, CC](seq: CC, value: T)(implicit conv: CC => Seq[T]) = seq.indexOf(value)
getIndex("abc", 'a')
Run Code Online (Sandbox Code Playgroud)
getIndex只要存在从其类可用的隐式转换,该方法就可以接收任何对象Seq[T].因此,我可以传递String给getIndex它,它会起作用.
在幕后,编译更改seq.IndexOf(value)为conv(seq).indexOf(value).
这是非常有用的,有一个语法糖来写它们.使用这种语法糖,getIndex可以像这样定义:
def getIndex[T, CC <% Seq[T]](seq: CC, value: T) = seq.indexOf(value)
Run Code Online (Sandbox Code Playgroud)
这种语法糖被描述为视图边界,类似于上限(CC <: Seq[Int])或下限(T >: Null).
请注意,视图边界已从2.11弃用,您应该避免使用它们.
隐式参数中的另一种常见模式是类型类模式.此模式允许为未声明它们的类提供公共接口.它既可以作为桥梁模式 - 获得关注点的分离 - 也可以作为适配器模式.
Integral您提到的类是类型类模式的典型示例.Scala标准库的另一个例子是Ordering.有一个库大量使用这种模式,称为Scalaz.
这是它的一个使用示例:
def sum[T](list: List[T])(implicit integral: Integral[T]): T = {
import integral._ // get the implicits in question into scope
list.foldLeft(integral.zero)(_ + _)
}
Run Code Online (Sandbox Code Playgroud)
它也有一个语法糖,称为上下文绑定,由于需要引用隐式,因此不太有用.该方法的直接转换如下所示:
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)(_ + _)
}
Run Code Online (Sandbox Code Playgroud)
当您只需将它们传递给使用它们的其他方法时,上下文边界会更有用.例如,该方法sorted对Seq需要的隐式Ordering.要创建一个方法reverseSort,可以写:
def reverseSort[T : Ordering](seq: Seq[T]) = seq.reverse.sorted
Run Code Online (Sandbox Code Playgroud)
因为Ordering[T]被隐式传递给reverseSort它,所以它可以隐式传递给它sorted.
当编译器看到需要隐式时,或者因为你正在调用一个在对象类上不存在的方法,或者因为你正在调用一个需要隐式参数的方法,它会搜索一个适合需要的隐式.
此搜索遵循某些规则,这些规则定义哪些隐含可见,哪些不可见.下表显示了编译器搜索implicits的位置,取自Josh Suereth关于implicits 的精彩演示,我衷心向所有想要提高Scala知识的人推荐.
让我们举个例子吧.
implicit val n: Int = 5
def add(x: Int)(implicit y: Int) = x + y
add(5) // takes n from the current scope
Run Code Online (Sandbox Code Playgroud)
import scala.collection.JavaConversions.mapAsScalaMap
def env = System.getenv() // Java map
val term = env("TERM") // implicit conversion from Java Map to Scala Map
Run Code Online (Sandbox Code Playgroud)
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)(_ + _)
}
Run Code Online (Sandbox Code Playgroud)
这与第一个示例类似,但假设隐式定义与其使用位于不同的文件中.另请参阅如何使用包对象来引入含义.
这里有两个对象伴侣.首先,查看"源"类型的对象伴随.例如,在对象内部Option有一个隐式转换Iterable,因此可以调用Iterable方法Option,或传递Option给期望的东西Iterable.例如:
for {
x <- List(1, 2, 3)
y <- Some('x')
} yield, (x, y)
Run Code Online (Sandbox Code Playgroud)
该表达式由编译转换为
List(1, 2, 3).flatMap(x => Some('x').map(y => (x, y)))
Run Code Online (Sandbox Code Playgroud)
但是,List.flatMap期待一个TraversableOnce,这Option不是.编译器然后查看Option对象伴随,并找到转换为Iterable,这是一个TraversableOnce,使这个表达式正确.
第二,预期类型的伴随对象:
List(1, 2, 3).sorted
Run Code Online (Sandbox Code Playgroud)
该方法sorted采用隐含的方式Ordering.在这种情况下,它查看对象内部Ordering,类的伴随Ordering,并在Ordering[Int]那里找到隐含.
请注意,还会查看超类的伴随对象.例如:
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"
Run Code Online (Sandbox Code Playgroud)
这是斯卡拉如何发现隐含的Numeric[Int],并Numeric[Long]在你的问题,顺便说一下,因为他们发现里面Numeric不是Integral.
这是使类型类模式真正起作用所必需的.例如Ordering,考虑它在它的伴随对象中带有一些含义,但是你不能添加东西.那么如何Ordering为自己的班级自动找到?
让我们从实现开始:
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)
}
}
Run Code Online (Sandbox Code Playgroud)
所以,考虑一下你打电话时会发生什么
List(new A(5), new A(2)).sorted
Run Code Online (Sandbox Code Playgroud)
正如我们所看到的,该方法sorted需要一个Ordering[A](实际上,它期望一个Ordering[B],在哪里B >: A).里面没有任何这样的东西Ordering,并且没有"源"类型可供查看.显然,这是里面找到它A,这是一个类型参数的Ordering.
这也是各种收集方法期望CanBuildFrom工作的方式:在伴随对象中找到的implicits类型参数CanBuildFrom.
我实际上没有看过这个例子.如果有人可以分享,我将不胜感激.原理很简单:
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"
Run Code Online (Sandbox Code Playgroud)
我很确定这是个玩笑.我希望.:-)
编辑
相关问题:
Jes*_*per 12
参数是implicit,这意味着Scala编译器将查看它是否可以找到一个隐式对象,它可以自动填入参数.
当你传入一个时Int,它会寻找一个隐含的对象,Integral[Int]它会找到它scala.math.Numeric.您可以查看源代码scala.math.Numeric,在哪里可以找到:
object Numeric {
// ...
trait IntIsIntegral extends Integral[Int] {
// ...
}
// This is the implicit object that the compiler finds
implicit object IntIsIntegral extends IntIsIntegral with Ordering.IntOrdering
}
Run Code Online (Sandbox Code Playgroud)
同样,有一个不同的隐含对象,Long它的工作方式相同.
| 归档时间: |
|
| 查看次数: |
4702 次 |
| 最近记录: |