プレースホルダは意外と難しい
プレースホルダーは、関数の引数を省略して書けるものだと思っていました。
結論からいうと、
プレースホルダはあくまで関数の部分適用をする物、
のようです。
※自分の中ではそう理解した。。。
具体的な例を挙げて、考えた事を説明していきます。
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>
なるほど。やっぱり、予想は正しかったようです。
という感じで、今日は終了したいと思います。