Posted at

Scala の implicit parameter のスコープと左辺値型推論で困った話

More than 3 years have passed since last update.

Scala の特徴的言語仕様の一つであり、憎まれたり便利だったりする implicit の小ネタです。いろいろ検証過程を書いてるので、結論だけ知りたい人は最後を見てね。


便利な implicit parameter

implicit には何種類かあるのはご存知という前提で。implicit parameter 便利ですよね。

implicit val g = "Hello "

def hoge(name: String)(implicit greet: String) {
println(greet + name)
}

hoge("Biacco") // 2番めのパラメタ群については implicit に渡される

ただ、この implicit が解決されるスコープと型推論が組み合わさるとややこしいことになるようです。


implicit parameter の解決スコープと型推論がうまく動かない例

今回ハマったのはこれ。

object Main extends App{

import Hoge._
val h = new Hoge
h.hoge
}

class Hoge {
def hoge(implicit s: String) { println(s) }
}

object Hoge {
implicit val hs = "hoge!"
}

一見動きそう。でもコンパイルすると

error: could not find implicit value for parameter s: String

なるほどわからん。


検証1: スコープを確認する

スコープに正しく入ってないのかな?と思って以下を試す。

object Main extends App{

import Hoge._
val h = new Hoge
h.hoge(hs) // 明示的に hs を渡してみた
}

class Hoge {
def hoge(implicit s: String) { println(s) }
}

object Hoge {
implicit val hs = "hoge!"
}

動くんですよねこれが。コンパイル通ります。おっかしいな〜〜〜〜〜〜〜〜〜〜


検証2: ローカルスコープにおいてみる

というわけで、スコープに存在しないわけではないというのがわかった。じゃあ間違いなくアクセスできるということで試しにローカルスコープにおいてみる。

object Main extends App{

import Hoge._

implicit val hs = "hoge!"

val h = new Hoge
h.hoge
}

class Hoge {
def hoge(implicit s: String) { println(s) }
}

object Hoge {
}

これもOK. この辺でかんべんしてくれという気持ちになり始める。


問題の定義: implicit な変数がスコープ内にいるにもかかわらず implicit parameter で解決されない

というわけで、現在の問題は


  • implicit な変数はスコープ内にいる

  • implicit parameter がそれを解決できない

  • implicit な変数はローカルスコープではなく import のスコープにいる

になります。一応 暗黙のパラメータ解決優先順位 なんかを見てみるものの、import した場合は Prefix なしの状態で参照できることとなっているが、 import Hoge._ しているのでそこも問題なさそうである。


解決法

まぁ、タイトルにもしているのでお気づきかと思いますが、この問題は implicit parameter と左辺値型推論が組み合わさると発生するようです。なので明示的に型注釈を与えてみます。

object Main extends App{

import Hoge._

val h = new Hoge
h.hoge
}

class Hoge {
def hoge(implicit s: String) { println(s) }
}

object Hoge {
implicit val hs: String = "hoge!" // implicit な変数に型注釈をつける
}

と、コンパイルできるようになりました!出力もちゃんと hoge! になる。なんだったんだこれ...

解決した後に見つけたんですが OE さんがもう書いてた。死にたい。

Scala コミュニティでは定期的に話題になる問題のようです。きびしい。

今回のは良くなかったね...