我在Scala示例中看到了一个名为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
。
另见How to investigate objects/types/etc. from Scala REPL?和Where does Scala look for implicits?
答案 0 :(得分:193)
以下是使用令人愉快的简单方法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.Ordering
隐式参数可以说是Scala比隐式视图更重要的特性。它们支持类型类模式。标准库在少数地方使用此功能 - 请参阅SeqLike#sorted
以及CanBuildFrom
中的使用方法。隐式参数也用于传递数组清单和A
实例。
Scala 2.8允许隐式参数的简写语法,称为Context Bounds。简而言之,类型参数为M[A]
的方法需要类型为def foo[A](implicit ma: M[A])
的隐式参数:
def foo[A: M]
可以改写为:
foo
但传递隐式参数但没有命名的重点是什么?在实现方法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)
。
{{1}}
答案 1 :(得分:191)
Implicitly
在Scala 2.8中可用,并在Predef中定义为:
def implicitly[T](implicit e: T): T = e
通常用于检查T
类型的隐式值是否可用并将其返回(如果是这种情况)。
来自retronym's presentation的简单示例:
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]
^
答案 2 :(得分:2)
“教你钓鱼”答案是使用Scaladoc nightlies中当前可用的字母成员索引。包/类窗格顶部的字母(以及非字母名称的#
)是指向以该字母开头的成员名称索引的链接(跨所有类)。如果您选择I
,例如,您会在implicitly
中找到Predef
一次出现的{{1}}条目,您可以从该链接访问该条目。
答案 3 :(得分:2)
启动 Scala 3 implicitly
已被改进的 summon
取代,其优点是能够返回比要求更多的 precise type
summon
方法对应于 Scala 2 中的隐式方法。它是
与Shapeless中的方法完全相同。区别
summon
(或 the)和 implicitly
之间是
比要求的类型更精确的类型。
例如给定以下类型
trait F[In]:
type Out
def f(v: Int): Out
given F[Int] with
type Out = String
def f(v: Int): String = v.toString
implicitly
方法会调用一个具有擦除类型成员 Out
scala> implicitly[F[Int]]
val res5: F[Int] = given_F_Int$@7d0e5fbb
scala> implicitly[res5.Out =:= String]
1 |implicitly[res5.Out =:= String]
| ^
| Cannot prove that res5.Out =:= String.
scala> val x: res5.Out = ""
1 |val x: res5.Out = ""
| ^^
| Found: ("" : String)
| Required: res5.Out
为了恢复类型成员,我们必须显式引用它,这违背了使用类型成员而不是类型参数的目的
scala> implicitly[F[Int] { type Out = String }]
val res6: F[Int]{Out = String} = given_F_Int$@7d0e5fbb
scala> implicitly[res6.Out =:= String]
val res7: res6.Out =:= String = generalized constraint
然而 summon
定义为
def summon[T](using inline x: T): x.type = x
没有这个问题
scala> summon[F[Int]]
val res8: given_F_Int.type = given_F_Int$@7d0e5fbb
scala> summon[res8.Out =:= String]
val res9: String =:= String = generalized constraint
scala> val x: res8.Out = ""
val x: res8.Out = ""
我们看到的类型成员 type Out = String
没有被删除,即使我们只要求 F[Int]
而不是 F[Int] { type Out = String }
。当 chaining dependently typed functions:
隐式调用的类型没有 Out
类型成员。为了这
原因,我们在使用依赖类型时应该避免隐式
功能。