Scala标识符“隐式”是什么?


169

我已经看到了implicitly在Scala示例中使用的命名函数。它是什么,如何使用?

这里的例子

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使用参数进行调用。

另请参阅如何调查对象/类型/等。来自Scala REPL?哪里斯卡拉寻找implicits?

Answers:


206

这是使用令人愉快的简单方法的一些原因implicitly

了解/排除隐式视图故障

当选择的前缀(例如,考虑到其中the.prefix.selection(args)不包含selection适用于该成员的成员args(即使尝试args使用隐式视图进行转换))时,也可以触发隐式视图。在当前或封闭范围(继承或导入)中,它们是从该类型的函数the.prefix到具有已selection定义或等效的隐式方法的函数。

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 2.8允许使用隐式参数的快捷语法,称为Context Bounds。简而言之,具有类型参数的方法A需要一个类型为隐式的参数M[A]

def foo[A](implicit ma: M[A])

可以重写为:

def foo[A: M]

但是,传递隐式参数而不命名它有什么意义呢?在实现该方法时这怎么有用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]

Person.PersonShow(si = implicitly, ss = Show.ShoutyStringShow).show(p)

2
scala> 1.min(2)res0:Int = 1在Scala 2.10.3中,我得到一个错误:scala>隐式[Int => {def min(i:Int):Any}] <console>:8:error:没有可用的Int => AnyRef {def min(i:Int):Any}隐式视图。隐式[Int => {def min(i:Int):任何}]
jhegedus

该答案将更新为最新版本。
emeth

1
隐式[Int => AnyVal {def min(i:Int):Int}]将起作用。应该固定在答案中。
Malkaviano

211

Implicitly在Scala 2.8中可用,在Predef中定义为:

def implicitly[T](implicit e: T): T = e

它通常用于检查类型的隐式T是否可用,并在这种情况下返回它

Retronym演示的简单示例:

scala> implicit val a = "test" // define an implicit value of type String
a: java.lang.String = test
scala> val b = implicitly[String] // search for an implicit value of type String and assign it to b
b: String = test
scala> val c = implicitly[Int] // search for an implicit value of type Int and assign it to c
<console>:6: error: could not find implicit value for parameter e: Int
       val c = implicitly[Int]
                         ^

6
该方法未完全检查;如果没有可用的隐式值,似乎会导致编译错误,如果有,则似乎会检索它。您能否提供更多有关为什么我要使用此内容的背景信息?
davetron5000

17
implicitly[Ordering[(Int, String)]].compare( (1, "b"), (1, "a") ),尤其是要检索上下文绑定引入的隐式参数:def foo[A: Ordering](a1: A, a2: A) = implicitly[Ordering[A]].compare(a1, a2)
Retronym 2010年

1
要在上面的视频链接中查看Retronym的讨论,请跳至13:50。
chaotic3quilibrium

-2

“教你钓鱼”的答案是使用Scaladoc晚间当前可用的字母成员索引。#包/类窗格顶部的字母(对于非字母名称,则为,)是指向以该字母开头(跨所有类)的成员名称的索引的链接。如果选择I,例如,您将在中找到implicitly带有一次出现的条目Predef,您可以从那里的链接访问它。


46
当然,那些scaladocs根本不讲任何隐含内容,因此几乎不算文档。有人将如何从这些文档中找出该方法的作用?我通常会因Scala文档而失望。像隐式这样的方法的行为远非显而易见,关于它们的文档仅比不存在更好。感谢上帝的堆栈溢出。/ end rant
Jeff


4
类型签名很好地证明了这一点。
Retronym 2010年

21
implicit在Scala中似乎是一个重要的语言功能,并且绝对值得进行适当的解释。认为文档仅详细说明类型签名计数似乎更像是智力上的自满,而不是真正的答案。查看操作员提出的特定问题-它是什么,以及如何使用?既没有回答这个问题,也没有提供您甚至没有提供实际链接的夜间文档。scala-lang.org/files/archive/nightly/docs/library / ... 这没什么用。有关真实文档的示例,请参见Niklaus Wirth或Turbo Pascal。-1
Thomas W

3
implicitimplicitly有关系,但非常不同。该implicit关键字是语言的一部分。implicitly在标准库的普通Scala代码中定义。由于在线文档包含源链接,因此我认为最好还是将提问者引向这些文档和链接的源。
Randall Schulz
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.