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

发布于 2024-09-25 16:24:14 字数 1559 浏览 1 评论 0原文

我看到 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) 意味着我们调用带参数隐式

另请参阅如何调查对象/类型/等。来自 Scala REPL?Scala 在哪里寻找隐式?

I have seen a function named implicitly used in Scala examples. What is it, and how is it used?

Example here:

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))
          ^

Note that we have to write implicitly[Foo[A]].apply(x) since the compiler thinks that implicitly[Foo[A]](x) means that we call implicitly with parameters.

Also see How to investigate objects/types/etc. from Scala REPL? and Where does Scala look for implicits?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

灵芸 2024-10-02 16:24:14

implicitly 在 Scala 2.8 中可用,并在 Predef as:

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]
                         ^

implicitly is avaliable in Scala 2.8 and is defined in Predef as:

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

It is commonly used to check if an implicit value of type T is available and return it if such is the case.

Simple example from 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]
                         ^
往事风中埋 2024-10-02 16:24:14

以下是隐式使用令人愉快的简单方法的一些原因。

了解/排除隐式视图

故障当选择的前缀(例如,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 允许使用隐式参数的简写语法,称为上下文边界。简而言之,带有类型参数 A 的方法需要类型为 M[A] 的隐式参数:

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

可以重写为:

def foo[A: M]

但是传递隐式参数有什么意义呢?不命名吗?这在实现 foo 方法时有何用处?

通常,不需要直接引用隐式参数,它将作为隐式参数通过隧道传递给另一个被调用的方法。如果需要,您仍然可以保留上下文绑定的简洁方法签名,并隐式调用来具体化值:

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)

Here are a few reasons to use the delightfully simple method implicitly.

To understand/troubleshoot Implicit Views

An Implicit View can be triggered when the prefix of a selection (consider for example, the.prefix.selection(args) does not contain a member selection that is applicable to args (even after trying to convert args with Implicit Views). In this case, the compiler looks for implicit members, locally defined in the current or enclosing scopes, inherited, or imported, that are either Functions from the type of that the.prefix to a type with selection defined, or equivalent implicit methods.

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

Implicit Views can also be triggered when an expression does not conform to the Expected Type, as below:

scala> 1: scala.runtime.RichInt
res25: scala.runtime.RichInt = 1

Here the compiler looks for this function:

scala> implicitly[Int => scala.runtime.RichInt]
res26: (Int) => scala.runtime.RichInt = <function1>

Accessing an Implicit Parameter Introduced by a Context Bound

Implicit parameters are arguably a more important feature of Scala than Implicit Views. They support the type class pattern. The standard library uses this in a few places -- see scala.Ordering and how it is used in SeqLike#sorted. Implicit Parameters are also used to pass Array manifests, and CanBuildFrom instances.

Scala 2.8 allows a shorthand syntax for implicit parameters, called Context Bounds. Briefly, a method with a type parameter A that requires an implicit parameter of type M[A]:

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

can be rewritten as:

def foo[A: M]

But what's the point of passing the implicit parameter but not naming it? How can this be useful when implementing the method foo?

Often, the implicit parameter need not be referred to directly, it will be tunneled through as an implicit argument to another method that is called. If it is needed, you can still retain the terse method signature with the Context Bound, and call implicitly to materialize the value:

def foo[A: M] = {
   val ma = implicitly[M[A]]
}

Passing a subset of implicit parameters explicitly

Suppose you are calling a method that pretty prints a person, using a type class based approach:

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)

What if we want to change the way that the name is output? We can explicitly call PersonShow, explicitly pass an alternative Show[String], but we want the compiler to pass the Show[Int].

Person.PersonShow(si = implicitly, ss = Show.ShoutyStringShow).show(p)
笑脸一如从前 2024-10-02 16:24:14

Scala 3隐式启动已被改进的summon取代,其优点是能够返回更多比要求的类型更精确

summon 方法隐式对应于 Scala 2 中。它是
和Shapeless中的方法一模一样。区别
summon (或)和 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

隐式方法将召唤一个带有已删除类型成员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 }。当 链接依赖类型函数

隐式调用的类型没有 Out 类型成员。为了这
原因是,我们在使用依赖类型时应该隐式避免
功能。

Starting Scala 3 implicitly has been replaced with improved summon which has the advantage of being able to return a more precise type than asked for

The summon method corresponds to implicitly in Scala 2. It is
precisely the same as the the method in Shapeless. The difference
between summon (or the) and implicitly is that summon can return a
more precise type than the type that was asked for.

For example given the following type

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 method would summon a term with erased type member 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

In order to recover the type member we would have to refer to it explicitly which defeats the purpose of having the type member instead of type parameter

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

However summon defined as

def summon[T](using inline x: T): x.type = x

does not suffer from this problem

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 = ""

where we see type member type Out = String did not get erased even though we only asked for F[Int] and not F[Int] { type Out = String }. This can prove particularly relevant when chaining dependently typed functions:

The type summoned by implicitly has no Out type member. For this
reason, we should avoid implicitly when working with dependently typed
functions.

水水月牙 2024-10-02 16:24:14

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

A "teach you to fish" answer is to use the alphabetic member index currently available in the Scaladoc nightlies. The letters (and the #, for non-alphabetic names) at the top of the package / class pane are links to the index for member names beginning with that letter (across all classes). If you choose I, e.g., you'll find the implicitly entry with one occurrence, in Predef, which you can visit from the link there.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文