Scala の implicit parameter のスコープと左辺値型推論で困った話
以下の記事は 2016 年 7 月 に Qiita に投稿した記事の移植です
pixivFANBOX にも投稿しています。ご支援頂ける場合はよろしくお願いいたします。
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 コミュニティでは定期的に話題になる問題のようです。きびしい。