1. 概要

Scalaプログラミング言語を勉強したり使用したりする人は誰でも、遅かれ早かれ暗黙的にに出くわします。 しかし、その意味を深く理解するために立ち止まる人はほとんどいません。

したがって、この記事では、暗黙的に関数のセマンティクスとその主な用途を分析します。

2. 「暗黙的に」の略

オブジェクトの質量と惑星の重力定数の値を指定して、オブジェクトの重量を返す関数を作成していると想像してください。

def weight(mass: Double, gravitationalConstant: Double): Double =
  mass * gravitationalConstant

weight 関数を少し使い始めた後、毎回重力定数値を明示的に渡すことは冗長であることがわかりました。

さらに、それはKISS(Keep It Simple、Stupid!)の原則に違反します。

ただし、コンパイラを使用して、重力定数値を入力することはできます。 そのために暗黙のパラメータを使用できます。

def weightUsingImplicit(mass: Double)(implicit gravitationalConstant: Double): Double =
  weight(mass, gravitationalConstant)

ここで、私たちがしなければならないのは、 weightUsingImplicit関数の解像度スコープでimplicit値として重力定数の値を提供することだけです(Scala[のImplicitパラメーターを参照) X215X]暗黙のパラメーターの詳細については):

implicit val G: Double = 9.81

しかし、私たちはもっとうまくやれると感じています。 カレーパラメータgravitationalConstantを明示的に指定する必要があるという事実は気に入らない。 Scalaはどのようなツールで私たちに力を与えてくれますか?

この点で、Scalaのバージョン2.8では、 Predef パッケージに新しい関数が導入されました。これは、コンパイラーがデフォルトでインポートするため、常に使用可能です。

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

基本的、 暗黙的に「暗黙のコンパイラ」として機能します。 タイプTの暗黙の値があるかどうかを確認できます 。 タイプTの暗黙的な値がスコープで使用できない場合、コンパイラーはその事実を警告します。

weight 関数の読みやすさを向上させるために、キラキラ光る新しいツールをどのように使用できますか? 暗黙のカレーパラメータを削除してから、暗黙的にの使用を導入できます。

def weightUsingImplicitly(mass: Double): Double = {
  val gravitationalConstant = implicitly[Double]
  weight(mass, gravitationalConstant)
}

基本的に、 Double 型の暗黙的な値を見つけて、それを変数gravitationalConstantの値として使用するようにコンパイラーに要求します。 涼しい。

ただし、上記の例は、Scalaでの暗黙的にの一般的な使用法ではありません。 実際、これは型クラスのパターンを構成するブリックの1つです。 紹介しましょう。

3. 型クラス:暗黙の解決の真の力を解き放つ

3.1. 型クラス101

Scalaで見つけた概念の多くは、Haskellプログラミング言語に由来することはよく知られています。 確かに、型クラスのパターンはその1つです。

基本的に、型クラスは、汎用型Tのクラスが持つ可能性のある動作または特性を表します。 これはインターフェースに似た概念であり、実際、Scalaではtraitを使用して表されます。

trait Searchable[T] {
  def uri(obj: T): String
}

特性の詳細については、記事Scalaの特性の概要を確認してください。

上記の例では、型クラスを定義して、型 T を検索可能、つまり、関連するURIを持つことができるようにしています。 Searchable 型クラスに参加するすべてのクラスは、その抽象メソッドuriを実装する必要があります。

型クラスは、Scala(そしてもちろんHaskell)がいわゆるアドホック多相を実装する方法を表します。 実際、 Searchable trait を実装する型を受け取るすべての関数は、ポリモーフィックな動作をする可能性があります。

def searchWithImplicit[S](obj: S)(implicit searchable: Searchable[S]): String = searchable.uri(obj)

CustomerPolicyの2つのタイプがあり、検索可能にしたいとします。

case class Customer(taxCode: String, name: String, surname: String)
case class Policy(policyId: String, description: String)

最初に行う必要があるのは、上記の2つのタイプにuriメソッドを実装することです。 それでは、匿名クラスを使用して Searchabletraitを実装しましょう。

implicit val searchableCustomer: Searchable[Customer] = new Searchable[Customer] {
  override def uri(customer: Customer): String = s"/customers/${customer.taxCode}"
}
implicit val searchablePolicy: Searchable[Policy] = new Searchable[Policy] {
  override def uri(policy: Policy): String = s"/policies/${policy.policyId}"
}

コンパイラの暗黙的な解決により、 searchWithImplicit メソッドの動作は、型クラスの解決されたインスタンスに応じて変化するため、ポリモーフィックになります。

Customer を使用してメソッドを呼び出す場合、コンパイラは変数 searchableCustomerを使用して型クラスを解決し、それ以外の場合は変数searchablePolicyを使用してを解決します。

型クラスのパターンの詳細については、Scalaの型クラスの記事を参照してください。

3.2. 型クラスと陰関数

さて、型クラスはかっこいいですが、暗黙的に関数はどこにありますか? すでに述べたように、を暗黙的にを「暗黙のコンパイラ」として使用できます。

ただし、今回は、CustomerPolicyタイプなどのタイプTを直接解決する必要はありません。 実際、タイプ Searchable [T] がある場合は、それを解決する必要があります

この問題を克服するために、Scalaはバージョン2.8からの型にいわゆる「コンテキストバウンド」を導入しています。 基本的に、これはタイプ T で真でなければならない制約であり、その構文はsearchWithImplicit関数のバリアントで表示できます。

def searchWithContextBound[S: Searchable](obj: S): String

お気づきのとおり、コンテキストバウンドはタイプSであり、タイプSearchable[S]が存在する必要があることをコンパイラーに通知します。

コンテキストに依存するため、パラメトリック関数の本体でタイプ Searchable [S] が使用可能であることが確実にわかっているため、を暗黙的に関数で使用できるようになりました。それを取得します:

def searchWithContextBound[S: Searchable](obj: S): String = {
  val searchable = implicitly[Searchable[S]]
  searchable.uri(obj)
}

ご覧のとおり、 searchWithContextBound 関数のシグネチャは、ビジネスパラメータのみを公開し、型クラスと暗黙の解決を完全に非表示にするため、searchWithImplicit関数のシグネチャよりもクリーンになりました。機構。

要約すると、型クラスパターンを使用すると、 searchWithContextBound関数の2つの異なる実装を使用できます。1つはCustomer型用で、もう1つはPolicy型用です。

val customer = Customer("123456", "Will", "Smith")
val uri = searchWithContextBound(customer)
assert(uri == "/customers/123456")

val policy = Policy("09876", "A policy")
val uri = searchWithContextBound(policy)
assert(uri == "/policies/09876")

最後になりましたが、新しい新しい型の関数の新しいポリモーフィックな動作を追加したい場合は、 Searchable trait の新しい匿名実装を追加して、魔法を起こさせてください!

4. 結論

この記事では、Scalaのバージョン2.8以降で使用可能な関数暗黙的にを紹介しました。 まず、「暗黙のコンパイラ」としての関数の基本的な使用法を示し始めました。

最後に、型クラスパターン内で暗黙的に関数を使用して、よりクリーンで読みやすくする方法を確認しました。

いつものように、記事の完全なソースコードは、GitHubから入手できます。