ScalaのFunctionが実装するメソッド

October 23, 2018
Function

しばらく放置してた。反省。

Scalaで関数を定義すると、すでにその時点でいくつかのメソッドが実装されている。 (レシーバとも言う?)

Function1 で実際にどんなメソッドを実装しているかを見てみた。 以下の4つを実装してるっぽい。

compose
andThen
apply
toString
method description
compose 関数の合成
andThen 関数の合成
apply 関数の適用
toString String型に変換

compose(関数合成を行うメソッド)

引数に func{n} を結合するメソッドを3つ compose してみる

object method extends App {
  def exampleFunc1(s: String): String = s + " func1"
  def exampleFunc2(s: String): String = s + " func2"
  def exampleFunc3(s: String): String = s + " func3"

  val ef1: String => String = exampleFunc1
  val ef2 = exampleFunc2 _
  val ef3 = exampleFunc3 _

  val f = ef1 compose ef2 compose ef3
  val r = f("EXAMPLE")
  println(r)
}
EXAMPLE func3 func2 func1

composeはレシーバのメソッドを後ろから実行するような動きをするみたい。

↓この部分は実際は

val f = ef1 compose ef2 compose ef3

↓こう呼び出される

val f = ef1.compose(ef2).compose(ef3)

順番的にはこうなる

1. ef1.compose(ef2).compose(ef3)
2. {ef2のあとef1が実行されるFunction1}.compose(ef3)
3. {ef3のあとef2のあとef1が実行されるFunction1}

andThen(関数合成を行うメソッド)

引数に func{n} を結合するメソッドを3つ andThen する

object method extends App {
  def exampleFunc1(s: String): String = s + " func1"
  def exampleFunc2(s: String): String = s + " func2"
  def exampleFunc3(s: String): String = s + " func3"

  val ef1: String => String = exampleFunc1
  val ef2 = exampleFunc2 _
  val ef3 = exampleFunc3 _

  val f = ef1 andThen ef2 andThen ef3
  val r = f("EXAMPLE")
  println(r)
}
EXAMPLE func1 func2 func3

andThenは composeとは逆の動きをする認識

ef1.andThen(ef2).andThen(ef3)
{ef1の後ef2を適用する関数}.andThen(ef3)
{ef1の後ef2の後ef3を適用する関数}

apply(コンストラクタ)

object method extends App {
  def exampleFunc1(s: String): String = s + " func1"

  val ef1: String => String = exampleFunc1
  val r1 = ef1.apply("EXAMPLE")
  val r2 = ef1("EXAMPLE")

  println(r1)
  println(r2)
}

r1 と r2 は同じ結果となる

toString

object method extends App {
  def exampleFunc1(s: String): String = s + " func1"

  val ef1: String => String = exampleFunc1
  println(ef1.toString())
}

InteliJで上記の toString を辿ると Function1 で以下のような実装であることがわかる

override def toString() = "<function1>"

でも結果は下記のようになる。。。謎

cop.method$$$Lambda$6/817406040@61009542

Function2(引数2つのメソッド)

これまで Function1 を例として見てきたけど、Function 2 では実装してるメソッドが異なる。

Function1 で実装されていた compore, andThen は実装されておらず、代わりに以下のメソッドが実装されている。

curried
tupled

curried(カリー化)

定義した関数の引数をまとめてカリー化が行える。

object method extends App {

  def f2(s1: String, s2: String): String = s1 + s2

  val f: (String, String) => String = f2

  // non curry
  val a1: String = f("EX", "AMPLE")
  println(a1)

  // curry
  val curry: String => String => String = f.curried
  val c1: String => String = curry("EX")
  val c2: String = c1("AMPLE")
  println(c2)
}

出力は以下のようになる

EXAMPLE
EXAMPLE

tupled(引数のタプル化)

引数をタプルで順番に受け取れるようにしてくれる。

object method extends App {

  def f2(s1: String, s2: String): String = s1 + s2

  val f: (String, String) => String = f2
  val t: (String, String) = ("EX", "AMPLE")


  // *** compile error ***
//  val r = f(t)
//  println(r)
  // tupled
  val tupled = f.tupled
  val tr: String = tupled(t)
  println(tr)
}

出力は以下のようになる

EXAMPLE

tupled 変数に型指定したけどなぜかエラーになる。よくわからない…。 tupled の型は恐らく (String, String) => String である。

compile error の部分は元の関数 f に対してタプルを渡している。 これは単純に型が違うのでエラーとなる。 tupledを呼び出す事でタプルで受け付ける関数に変換できる。

触ってみたまとめ

scalaは全てオブジェクトになっていて、関数ですらオブジェクトになっている。 こういう細かい実装されてる関数に詳しくなってscalaの使いたい機能をサクサク使えるようになりたい。