[Scala] メソッドから FunctionN への変換 eta-expansion

2018/03/01   #Scala 
このエントリーをはてなブックマークに追加

例えばこういうの。

scala> case class Hoge(name: String) extends AnyVal
defined class Hoge

scala> def hoge(f: String => Hoge): Hoge = f("hoge")
hoge: (f: String => Hoge)Hoge

scala> hoge(Hoge)
res1: Hoge = Hoge(hoge)

hoge メソッドの引数は f: String => Hoge 、すなわち「String をとって Hoge を返す関数」であるが、 hoge(Hoge) と case class を指定しても正しく動く。最初はおやっと思いました。

scalaのeta-expansionってなんなのかようやくわかった | KentaKomai Blog

メソッドから FunctionN への変換、これを eta-expansion というらしい。

Scala の関数 - tkawachi Blog

FunctionN が型として要求される箇所にメソッドを書いても同じように eta expansion される。

FunctionN が型として要求される箇所にメソッドを書いたら、この eta-expansion が自動的に行われるらしい。

最初の例に戻ると、 hoge(Hoge) は、メソッド引数で FunctionN が型として要求される箇所に case class を渡しているように見えるが、
case class を渡しているのではなく、case class の apply メソッドを渡している形になる。
この case class の apply メソッドが eta-expansion されて、String => Hoge な FunctionN になっているので正しく動くということみたい。
apply を明示的に書いてやってみると同じ結果になる。

scala> hoge(Hoge.apply)
res2: Hoge = Hoge(hoge)

番外編: メソッドオーバーロードとeta-expansion

多相な関数の定義から学ぶ、型クラスデザインパターン

#引数オーバーロードのデメリット ##(多相のままでは)eta-expansionできない これにより関数合成、変換、など様々な操作ができなくなる

オーバーロードされたメソッドをeta-expansionする - kmizuの日記

object O {
  def add(x: Double, y: Int): Double = x + y
  def add(x: Int, y: Double): Double = x + y
}
O.add _:((Double, Int) => Double)

以前 Scala 界隈の強い人から「オーバーロードはいろいろあるから推奨しない、別メソッドにしたほうがいい」的なことを聞いたことがあり、なんでかなと思ってたけどこういうことが理由なのかな?