成らぬは人の為さぬなりけり

エンジニアライフをエンジョイする為のブログ

プレースホルダは意外と難しい

プレースホルダーは、関数の引数を省略して書けるものだと思っていました。

結論からいうと、
プレースホルダはあくまで関数の部分適用をする物、
のようです。
※自分の中ではそう理解した。。。

具体的な例を挙げて、考えた事を説明していきます。

  val list = List(1,2,3)
  list.foreach(i => println(i))

これは

  val list = List(1,2,3)
  list.foreach(println _)

こう書けるので、関数の引数を省略して書けるものだと思っていました。
しかし、この理解は間違ってました。


例えば、、、

  val list = List(1,2,3)
  list.foreach(_.toString)

これはOKですが

  val list = List(1,2,3)
  list.foreach(println(_.toString))

これはコンパイルエラーになります。

scala> list.foreach(println(_.toString))
<console>:9: error: missing parameter type for expanded function ((x$1) => x$1.toString)
              list.foreach(println(_.toString))
                                   ^

このエラーを見ると、パラメータの型が無いからダメ、と言っているように見えるので、
じゃあ型を指定してやれ!ということで
こうしてみました。

scala> list.foreach(println((_:Int).toString))
<console>:9: error: type mismatch;
 found   : Unit
 required: Int => ?
              list.foreach(println((_:Int).toString))
                                  ^

エラーの内容が変わりました。
「Int=>?」型じゃないとイケないけど、
「Unit」だよ。
と、怒られています。


どういうことが起こっているんでしょうか?
分からなかったので、分解してみました。

scala> (_:Int).toString
res53: Int => java.lang.String = <function1>

これは、Intを引数に取って、Stringを返す1引数関数になっているようです。
次に

scala> println((_:Int).toString)
<function1>

お、、、これはどういう意味でしょうか???
変数に代入してみました。

scala> val hoge = println((_:Int).toString)
<function1>
hoge: Unit = ()

「hoge」はUnitを返す関数になっているようです。
printlnのシグネチャを見てみます。
scala.Predefに定義されています。

def println(x: Any): Unit 

なるほど。引数を取ってUnitを返すようになっています。

ちなみに、foreachも見てみました。

 def foreach(f: (A) ⇒ Unit): Unit

1つ引数を取ってUnitを返す関数を要求しています。

つまり、

  val list = List(1,2,3)
  list.foreach(println((_:Int).toString))

このforeachはListの要素の型がIntなので
「Intを引数に取ってUnitを返す関数」を要求しているのに対して、
Unitを渡そうとしているから怒られているわけでした。

つまり、

println(_)

は、「何か引数を取って、それをprintln関数に適用する」という関数になるのでOKだった・・・
のか??

scala> println(_)
<console>:8: error: missing parameter type for expanded function ((x$1) => println(x$1))
              println(_)
                      ^

いや、怒られます。
foreachの引数の型からプレースホルダの型が推論できたからOKだった・・・のか?

scala> println(_:Int)
res62: Int => Unit = <function1>

これはOKですが。

scala> println(_):(Int => Unit)
<console>:8: error: missing parameter type for expanded function ((x$1) => (println(x$1): _root_.scala.Function1[Int, Unit]))
              println(_):(Int => Unit)
                      ^
<console>:8: error: type mismatch;
 found   : Unit
 required: Int => Unit
              println(_):(Int => Unit)
                     ^

これはダメ。
「found : Unit」ということは部分適用された関数になってないのか??

scala> def hoge(x:Int => Unit) = x(100)
hoge: (x: Int => Unit)Unit

scala> hoge(println _)
100

しかしこれはOK。
うーん、、、

と思ったら、

scala> println _ :(Int => Unit)
res66: Int => Unit = <function1>

これはOK、という事が判明しました。
むむ、もしや、、、

scala> (println(_)):(Int => Unit)
res69: Int => Unit = <function1>

なるほど。やっぱり、予想は正しかったようです。

という感じで、今日は終了したいと思います。