Scalaでの暗黙の推論

多くの初心者はそうではありませんが、Scala開発者は暗黙を適度に有用な機能と見なしています。使用は通常、への受け渡しExecutionContext



 に制限されていFuture



ます。他の人は暗黙のことを避け、機会を有害であると見なします。





このようなコードは多くの人を怖がらせます:





implicit def function(implicit argument: A): B
      
      



しかし、このメカニズムは言語の重要な利点だと思います。その理由を見てみましょう。





暗黙について簡単に

一般に、implicitは、コンパイル中にコードを自動的に完了するためのメカニズムです。





  • 暗黙の引数の場合、値は自動的に置き換えられます





  • 暗黙的な変換の場合、値はメソッド呼び出しで自動的にラップされます





言語の作成者からのこのビデオの視聴に興味を持っているこのトピックについては、深く掘り下げません要するに、この1つのメカニズムはいくつかの異なるケースで使用されます(そしてScala 3ではいくつかの別々の機能に分割されます):





  1. 暗黙のコンテキストを渡す(のようなExecutionContext







  2. 暗黙の変換(非推奨)





  3. 拡張メソッド(既存の型にメソッドを追加するためのシンタックスシュガー)





  4. 型クラスと暗黙の解決





型クラスと暗黙の推論

型クラス自体は、目新しさや革命をもたらすことはありません。型自体の「内部」ではなく、型の「for」メソッドの実装にすぎません。これは、Comparable



 &Ordering



Comparator



 Javaの場合)の違いのようなものです。





Comparable



  :





class Person(age: Int) extends Comparable[Person] {
  override def compareTo(o: Person): Int = age compareTo o.age
}
def max[T <: Comparable[T]](xs: Iterable[T]): T = xs.reduce[T] {
  case (a, b) if (a compareTo b) < 0 => b
  case (a, _) => a
}
      
      



Ordering



  , :





case class Person(age: Int)

implicit object ByAgeOrdering extends Ordering[Person] {
  override def compare(o1: Person, o2: Person): Int = o1.age compareTo o2.age
}
def max[T: Ordering](xs: Iterable[T]): T = xs.reduce[T] {
  case (a, b) if Ordering[T].lt(a, b) => b
  case (a, _) => a
}
// is syntactic sugar for
def max[T](xs: Iterable[T])(implicit evidence: Ordering[T]): T = ...
      
      



.





. :





implicit val value: A = ???
implicit def definition: B = ???
implicit def conversion(argument: C): D = ???
implicit def function(implicit argument: E): F = ???
      
      



? conversion



, , : value



, definition



 & function



  . : val



 , def



  . – .





– , , .





– , – , :





implicit def pairOrder[A: Ordering, B: Ordering]: Ordering[(A, B)] = {
  case ((a1, b1), (a2, b2)) if Ordering[A].equiv(a1, a2) => Ordering[B].compare(b1, b2)
  case ((a1,  _), (a2,  _)) => Ordering[A].compare(a1, a2)
}
// again, just syntactic sugar for:
implicit def pairOrder[A, B](implicit a: Ordering[A], b: Ordering[B]): Ordering[(A, B)] = ...
      
      



, :





val values = Seq(
  (Person(30), ("A", "A")),
  (Person(30), ("A", "B")),
  (Person(20), ("A", "C"))
)
max(values) // => (Person(30),(A,B))
      
      



Seq[(Person, (String, String))]



  Ordering



  :





max(values)(
  pairOrder(
    ByAgeOrdering, 
    pairOrder(Ordering.String, Ordering.String)
  )
)
      
      



したがって、暗黙的な推論により、一般的な推論規則を記述し、これらの規則を組み合わせて型クラスの特定の実装を取得するようにコンパイラーに指示できます。独自の型または独自のルールを追加する場合、最初からすべてを記述する必要はありません。コンパイラーはすべてを組み合わせて、目的のオブジェクトを取得します。





そして最も重要なことは、コンパイラが失敗した場合、ランタイムエラーではなくコンパイルエラーが発生し、問題をすぐに修正できることです。もちろん、軟膏の軟膏にはハエがありますが(コンパイラが失敗した場合、チェーン内のどのリンクが欠落していたかはわかりません)、これをデバッグするのは必ずしも簡単ではありません。





うまくいけば、暗黙的がもう少し明示的になります。








All Articles