戦略 Scala 日記

素人プログラマの思考のセンス

形式知にして使う、暗黙のimplicit

「暗黙のXX」と訳される、Scalaの"implicit"キーワード。 しかし、その動作をちゃんと理解しないままでも、

Scalaのimplitiキーワード(暗黙のXX)が使われるのは次の2つの場合。

  • 暗黙の型変換
  • 暗黙の引数

これだけなのだが、初学者にとってややこしく感じるのは、これらが実装において意識をしなくとも暗黙のうちに動いてしまうからではないだろうか。

暗黙の型変換

暗黙の型変換の場合は、型が要求される状況で異なる型を渡したとしてもこれを自動的に変換するというもの。 例えば次の用に、String型を要求する変数に、Int型の9999を代入することはできない。

scala> val str: String = 9999
<console>:7: error: type mismatch;
 found   : Int(9999)
 required: String
       val str: String = 9999
                         ^

ここで、Int型の値をString型に変換する関数にimplicitキーワードをつけて予め定義しておく。こうすることで暗黙の型変換が行われ、9999がStringに自動的に変換され先のstrに代入可能となる。

scala> implicit def intToString(value: Int): String = value.toString
warning: there was one feature warning; re-run with -feature for details
intToString: (value: Int)String

scala> val str: String = 9999
str: String = 9999

暗黙の引数

こちらは関数(もしくはメソッド)の引数を予め定義しておくことで、メソッドを呼び出すときにその引数リストを省略することができるというもの。 引数にデフォルト値を設定する場合と、同じように予め値を定義する役割なのだが、関数(メソッド)の呼び出し時にその値が決定するという点で異なる。引数のデフォルト値は関数の定義時にその値が決定される。

状況によって値が変わるそんな引数を束縛するメソッドとはどんな場合があるか

class Tax {
  def tax(implicit rate: Int) = rate * 0.01
} 

class VAT {
  implicit val vat = 5
  
  def calc = {
    val tax = new Tax
    tax.tax     // 暗黙の引数を取り込み、0.05が返される
    tax.tax(10) // 明示的に引数に10を渡しているので、0.1が返される
  }
}

暗黙の引数を引数に用いる場合

例えば下記は、ScalaのORMであるSlickのデータベースへの接続を持った状態での一連の処理内容を記述するような場合に使う記述である。

val result = db.withSession {  implicit session =>
  query.list // <- takes session implicitly
}

ここで、listは暗黙の引数としてsessionを受け取るので、withSessionが受け取る関数引数は、implicit sessionを受け取るようにしている。