174

implicitlyScala の例で使用されているという名前の関数を見たことがあります。それは何で、どのように使用されますか?

ここに例

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から?そしてScalaはどこで暗黙を探しますか?

4

4 に答える 4

221

implicitlyScala 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]
                         ^
于 2010-10-04T13:22:34.073 に答える
210

楽しくシンプルな方法を使用する理由はいくつかありますimplicitly

暗黙のビューを理解/トラブルシューティングするには

暗黙的ビューは、選択範囲のプレフィックス(たとえば、(暗黙的ビューで変換しようとした後でも)に適用可能なthe.prefix.selection(args)メンバーが含まれていない場合にトリガーできます)。この場合、コンパイラーは、ローカルで定義された暗黙的メンバーを検索します。現在のスコープまたはそれを囲むスコープ内で、継承またはインポートされた、そのタイプから定義されたタイプへの関数、または同等の暗黙的なメソッドのいずれか。selectionargsargsthe.prefixselection

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#sortedCanBuildFrom暗黙的パラメーターは、配列マニフェストとインスタンスを渡すためにも使用されます。

Scala 2.8では、コンテキスト境界と呼ばれる暗黙的なパラメーターの簡略構文を使用できます。簡単に言うと、型Aの暗黙的なパラメータを必要とする型パラメータを持つメソッド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)
于 2010-10-04T22:05:44.547 に答える
3

Scala 3 を開始すると、要求されたよりも正確な型を返すことができるという利点があるimplicitly改良型に置き換えられました。summon

summonメソッドは Scala 2 で暗黙的に対応します。Shapeless でのメソッドとまったく同じです。summon(または) との違いimplicitlyは、 Summon は要求された型よりも正確な型を返すことができるということです。

たとえば、次のタイプの場合

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 }

によって暗黙的に呼び出された型には、Out型メンバーがありません。このため、依存型の関数を使用する場合は、暗黙的に回避する必要があります。

于 2021-04-17T21:23:30.343 に答える
-4

「釣り方を教える」答えは、Scaladoc nightliesで現在利用可能なアルファベット順のメンバー インデックスを使用することです。パッケージ/クラス ペインの上部にある文字 (および#アルファベット以外の名前の場合は ) は、その文字で始まる (すべてのクラスの) メンバー名のインデックスへのリンクです。Iたとえば、を選択すると、implicitlyに 1 回出現するエントリが表示されPredef、そこのリンクからアクセスできます。

于 2010-10-04T13:50:19.063 に答える