我implicitly在 Scala 示例中看到了一个名为 used 的函数。它是什么,它是如何使用的?
implicitly
这里的例子:
scala> sealed trait Foo[T] { def apply(list : List[T]) : Unit }; object Foo { | implicit def stringImpl = new Foo[String] { | def apply(list : List[String]) = println("String") | } | implicit def intImpl = new Foo[Int] { | def apply(list : List[Int]) = println("Int") | } | } ; def foo[A : Foo](x : List[A]) = implicitly[Foo[A]].apply(x) defined trait Foo defined module Foo foo: [A](x: List[A])(implicit evidence$1: Foo[A])Unit scala> foo(1) <console>:8: error: type mismatch; found : Int(1) required: List[?] foo(1) ^ scala> foo(List(1,2,3)) Int scala> foo(List("a","b","c")) String scala> foo(List(1.0)) <console>:8: error: could not find implicit value for evidence parameter of type Foo[Double] foo(List(1.0)) ^
请注意,我们必须编写implicitly[Foo[A]].apply(x),因为编译器认为这 implicitly[Foo[A]](x)意味着我们implicitly使用参数调用。
implicitly[Foo[A]].apply(x)
implicitly[Foo[A]](x)
以下是使用令人愉快的简单方法的几个原因implicitly。
隐式视图可以在选择的前缀时触发(例如,考虑the.prefix.selection(args)不包含selection适用于的成员args(即使在尝试args使用隐式视图进行转换后)。在这种情况下,编译器会查找本地定义的隐式成员在当前或封闭范围内,继承或导入,它们是从该类型the.prefix到具有已selection定义类型的函数或等效的隐式方法。
the.prefix.selection(args)
selection
args
the.prefix
scala> 1.min(2) // Int doesn't have min defined, where did that come from? res21: Int = 1 scala> implicitly[Int => { def min(i: Int): Any }] res22: (Int) => AnyRef{def min(i: Int): Any} = <function1> scala> res22(1) // res23: AnyRef{def min(i: Int): Int} = 1 scala> .getClass res24: java.lang.Class[_] = class scala.runtime.RichInt
当表达式不符合预期类型时,也可以触发隐式视图,如下所示:
scala> 1: scala.runtime.RichInt res25: scala.runtime.RichInt = 1
编译器在这里寻找这个函数:
scala> implicitly[Int => scala.runtime.RichInt] res26: (Int) => scala.runtime.RichInt = <function1>
隐式参数可以说是 Scala 比隐式视图更重要的特性。它们支持类型类模式。标准库在几个地方使用了它——看看scala.Ordering它是如何使用的SeqLike#sorted。隐式参数也用于传递数组清单和CanBuildFrom实例。
scala.Ordering
SeqLike#sorted
CanBuildFrom
Scala 2.8 允许隐式参数的简写语法,称为上下文边界。简而言之,具有类型参数的方法A需要类型的隐式参数M[A]:
A
M[A]
def foo[A](implicit ma: M[A])
可以改写为:
def foo[A: M]
但是传递隐式参数但不命名它有什么意义呢?这在实现该方法时有何用处foo?
foo
通常,不需要直接引用隐式参数,它将作为隐式参数通过隧道传递给另一个被调用的方法。如果需要,您仍然可以使用 Context Bound 保留简洁的方法签名,并调用implicitly以实现该值:
def foo[A: M] = { val ma = implicitly[M[A]] }
假设您正在调用一个漂亮地打印一个人的方法,使用基于类型类的方法:
trait Show[T] { def show(t: T): String } object Show { implicit def IntShow: Show[Int] = new Show[Int] { def show(i: Int) = i.toString } implicit def StringShow: Show[String] = new Show[String] { def show(s: String) = s } def ShoutyStringShow: Show[String] = new Show[String] { def show(s: String) = s.toUpperCase } } case class Person(name: String, age: Int) object Person { implicit def PersonShow(implicit si: Show[Int], ss: Show[String]): Show[Person] = new Show[Person] { def show(p: Person) = "Person(name=" + ss.show(p.name) + ", age=" + si.show(p.age) + ")" } } val p = Person("bob", 25) implicitly[Show[Person]].show(p)
如果我们想改变名字的输出方式怎么办?我们可以显式调用PersonShow,显式传递一个替代Show[String],但我们希望编译器传递Show[Int].
PersonShow
Show[String]
Show[Int]
Person.PersonShow(si = implicitly, ss = Show.ShoutyStringShow).show(p)