Scalaの抽出子(apply, unapply)サンプルと呼ばれるタイミング

# apply, unapply
ScalaのObject内で良くセットで定義されるメソッド、
`apply` (注入、injection)、`unapply`(抽出、extraction)。

細かい事はコップ本26章やその他の有用な記事を見るとして...

`unapply`メソッドを持つオブジェクトは**抽出子(extractorObject)**と呼ばれる。

# 例1

```scala:例1
object extractorObject {

  // 抽出子オブジェクト
  object Sample {

    def apply(str: String): String = {
      // 文字列を作った
      str + " (applyが呼ばれた)"
    }

    // ここでの引数strはapplyが作り出した文字列
    def unapply(str: String): Option[String] = {
      if (str.nonEmpty) Some(str + (" (unapplyも呼ばれた)")) else None
    }
  }

  def main(args: Array[String]): Unit = {
    val p = Sample("本日は晴天なり") // Sample#applyを呼ぶ
    println(p)

    p match {
      case Sample(s) => println(s) // Sample#unapplyを呼ぶ
      case _ => println("No match")
    }
  }
}

// 本日は晴天なり (applyが呼ばれた)
// 本日は晴天なり (applyが呼ばれた) (unapplyも呼ばれた)
```

例1では次のような順序で、`apply` `unapply` が実行される。

*  mainメソッドで `Sample("任意の文字列")` を生成する
  * `apply`が呼ばれる  
  * `apply`は任意の文字列を受け取って、`「任意の文字列 (applyが呼ばれた)」`という文字列を作る。
* パターンマッチが抽出子オブジェクトを参照するパターンを検出する
  * `unapply`が呼ばれる
     * `Sample.unapply(str)` が実は呼び出されている
  * `unapply`は`apply`の**構築プロセスを逆戻すような働きをし**、引数に`「任意の文字列 + (applyが呼ばれた)」`という文字列を受取り、`「任意の文字列 (applyが呼ばれた) (unapplyも呼ばれた)」`という文字列を返す


# 例2

例2は、木についてる葉っぱの数を数えるもの。とある木(treeA)に、枝(Branch)があり葉っぱ(Leaf)が数枚ついている。[参考](https://www.scala-lang.org/old/node/53.html)

```scala:例2
object extractorObject {

  abstract class Tree

  // 枝クラス
  class Branch(val left: Tree, val right: Tree) extends Tree

  // 枝オブジェクト
  object Branch {

    def apply(left: Tree, right: Tree): Tree = {
      println("Branch#apply 呼ばれたよ")
      new Branch(left, right)
    }

    def unapply(x: Tree): Option[(Tree, Tree)] = {
      println("Branch#unapply 呼ばれたよ")
      x match {
        case y: Branch => Some(y.left, y.right)
        case _ => None
      }
    }
  }

  // 葉クラス
  class Leaf(val x: Int) extends Tree

  // 葉オブジェクト
  object Leaf {

    def apply(x: Int): Tree = {
      println("Leaf#apply 呼ばれたよ" + "x = " + x.toString)
      new Leaf(x)
    }

    def unapply(x: Tree): Option[Int] = {
      println("Leaf#unapply 呼ばれたよ")
      x match {
        case y: Leaf => Some(y.x)
        case _ => None
      }
    }
  }

  // 木A
  val treeA = Branch(Branch(Leaf(1), Leaf(2)), Branch(Leaf(3), Leaf(4)))

  def sumLeaves(t: Tree): Int = t match {
    case Branch(l, r) => sumLeaves(l) + sumLeaves(r)
    case Leaf(x) => x
  }

  // 実行
  def main(args: Array[String]): Unit = {
    println("sum of leaves = " + sumLeaves(treeA))
  }
}

// 結果

//  Leaf#apply 呼ばれたよ x = 1
//  Leaf#apply 呼ばれたよ x = 2
//  Branch#apply 呼ばれたよ
//  Leaf#apply 呼ばれたよ x = 3
//  Leaf#apply 呼ばれたよ x = 4
//  Branch#apply 呼ばれたよ
//  Branch#apply 呼ばれたよ

//  Branch#unapply 呼ばれたよ
//  Branch#unapply 呼ばれたよ
//  Branch#unapply 呼ばれたよ
//  Leaf#unapply 呼ばれたよ
//  Branch#unapply 呼ばれたよ
//  Leaf#unapply 呼ばれたよ
//  Branch#unapply 呼ばれたよ
//  Branch#unapply 呼ばれたよ
//  Leaf#unapply 呼ばれたよ
//  Branch#unapply 呼ばれたよ
//  Leaf#unapply 呼ばれたよ

//  sum of leaves = 10

```

例2では 

* `val treeA = Branch(Branch(Leaf(1), Leaf(2)), Branch(Leaf(3), Leaf(4)))`
  * `treeA` に枝葉のオブジェクトが作成される
  * 作成順に従って、`apply`が呼ばれているのが分かる
* `sumLeaves(treeA)`
  * `case Branch(l, r)` のマッチングが行われる
  * `Branch.unapply(Branch)`が呼ばれる
  * `Some(Branch, Branch)`が返り、`sumLeaves`の`case Branch(l, r)`の`l`,`r`にそれぞれ束縛される
* `sumLeaves(treeA)`
  * `case Leaf(x)`のマッチングが行われる
  * `Leaf.unapply(Leaf)`が呼ばれる 
  * `Some(Int)` が返り、`case Leaf(x)`の`x`に束縛される
* Leafの1 + 2 + 3 + 4 = 10が計算される 

なお、`枝クラス`、`枝オブジェクト`、`葉クラス`、`葉オブジェクト`をすべて取っ払って代わりに、

```scala
  case class Branch(left: Tree, right: Tree) extends Tree
  case class Leaf(x: Int) extends Tree
```

としても、ちゃんと動く。

`apply`はなんとなく分かるのですが、`unapply`の挙動がイマイチ分からなかったので調べてみました:kissing: