LoginSignup
9
5

More than 5 years have passed since last update.

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

Posted at

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 コミュニティでは定期的に話題になる問題のようです。きびしい。

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

9
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
5