関数型プログラミング入門者向け。Scalaと関数型プログラミングで一番重要な概念： Part 2 部分適用とカリー化

[関数型プログラミング入門者向け。Scalaと関数型プログラミングで一番重要な概念： Part 1 関数合成](https://qiita.com/yoshiwatanabe/items/dcb081689f99d48e2f28) からの続きです。

# Part 2 部分適用とカリー化

部分適用とカリー化は混同されがちだけど、違うものです。部分適用はカリー化とは関係なく出来ますし、カリー化された関数は部分適用を使わずに利用することができます。一緒に使われることが多いようですが、お互いに依存はしていみせん。

混乱を解きほぐすために、まずはカリー化の話題は後回しにして、伝統的な複数の引数を取る関数を使って部分適用をみていきます。次に、カリー化された関数の定義の仕方をおさらいします。その後、*カリー化された関数と部分適用*について解説します。そして最後に*カリー化された関数と合成関数*についてみていきます。

高階関数は部分適用や関数合成とともに利用されることが多いです。その際、カリー化された関数が使わる場合もあるし、無い場合もあるでしょう。高階関数は別に解説する予定です。

## 複数の引数を取る関数を特殊化（specialize）する

部分適用機能を使って、ある関数の引数の（すべてではなく）いくつかを指定することで、新しい関数を定義できます。

この機能の使い方を、まずは１つ以上の引数を取る関数を使ってみていきましょう。

引数を3つ取る、ごく簡単な ```f``` という関数を定義します。

```scala
def f(a: Int, b: Int, x: Int):Int = a * x + b
```

関数の呼び出しはいつもどおり。

```scala
scala> f(2, 5, 3)
res81: Int = 11
```

さてここで、引数 ```a``` の値が ```2``` であるとが、早い段階で分かっていたならば、以下ように出来ます。

```scala
scala> def f2 = f(2, _, _)

scala> f2(5, 3)
res85: Int = 11
```

ここでは、アンダースコア記号は引数 ```b``` と ```x``` への未知の値を表します。対して、引数 ```a``` については値 ```2``` を関数 ```f``` に部分適用して新たな関数を作り出し ```f2``` という名前で定義しました。関数 ```f2``` の呼び出すには引数は２つだけ渡せば済みます。 

さらに別の状況を見てみましょう。もし引数 ```b``` の値だけがすでに分かっているとしたらどうでしょう？ (値は ```5```とします) 

多分、すぐに分かったと思いますが・・・このようになります。

```scala
scala> def f3 = f(_, 5, _)
f3: (Int, Int) => Int

scala> f3(2, 3)
res84: Int = 11
```

```f3``` は２つの引数を取りますが、それは ```a``` と ```x``` に対応します。値 ```5``` はすでに ```b``` の引数として固定されています。

では、 ```a``` と ```b``` の両方の値がすでに分かっていて、未知のものは ```x``` だけだった場合、どうなるかはもうすでにお分かりでしょう。

```scala
scala> def f4 = f(2, 5, _)
f4: Int => Int

scala> f4(3)
res86: Int = 11
```

## カリー化の仕方

まずは、前のセクションで使った関数を*カリー化された状態*にすることから始めます。

ここで改めて確認ですが、カリー化と部分適用はそれぞれ独立した概念であり、一緒に利用される*場合もある*けれど、依存関係にはありません。

Scala では、カリー化された関数は、このように記述できます。

```scala
scala> def fc(a:Int)(b:Int)(x:Int) = a * x + b
fc: (a: Int)(b: Int)(x: Int)Int
```

カリー化された関数 ```fc``` の実際の振る舞いは、前述の複数の引数を取る関数 ```f``` と全く同じです。

すぐに気づく点は、引数の指定の仕方です。```a```, ```b```, そして ```x```それぞれの引数が独立したカッコのペアで囲まれています。こうすることで、関数 ```fc``` は引数を１つだけとり、戻り値として別の関数を返すようになります。

分かりやすいように、複数の引数を取る関数 ```f``` とカリー化された関数 ```fc``` を比較してみましょう。

```scala
scala> f _
res96: (Int, Int, Int) => Int = $$Lambda$1172/0x0000000801894040@ffa7bed

scala> fc _
res97: Int => (Int => (Int => Int)) = $$Lambda$1173/0x0000000801893840@4e843bf6
```

複数の引数をとる関数の方はよく見慣れた感じです。 ```(Int, Int, Int) => Int``` の部分は *「この関数はInt型の引数を３つ取り、Int型の戻り値を返す」* を意味します。

カリー化された関数（引数を1つしかとらない方) は様子が随分と異なります。 ```Int => (Int => (Int => Int))``` の部分は *「この関数はInt型の引数を１つ取り、戻り値として```(Int => (Int => Int))```型の関数を返す」* という意味になります。

実は、このブログ記事はもともと英語で書いたんですが、このカリー化された関数の型を表すにい日本語は大変不向きです。英語で表現するならもっと自然です。

> *it is a function that takes a single argument of Int and returns a function that takes a single argument of Int type and returns a function that takes a single argument of Int type and returns a value of Int type."*

（読み方のコツは、```=>``` を見つけたら ```=>``` の左側について「a function that takes... and returns (a function that...)」と始めることです。英語だとうまく入れ子の状態とマッチして文に出来ます。）

ここで鍵になる考え方は 「・・・引数を１つ取り・・・」という部分です（英語の"...takes a single argument..."の部分。英語では引数の数と同じだけ、"...takes a single argument..." という表現が出現しているのが分かると思います（日本語だと難しい・・・）。

ということで、関数 ```fc``` は引数を１つ求めているので、ある値を渡したとすると、戻り値として```(Int => (Int => Int))```という型の関数を返します。

カリー化された関数では、２つ以上の引数を一度に渡すことはありませんし、それは入れ子状態の関数のどのレベルでも同じです。（2つ以上の値を「組（tuple)」として渡す場合はあるかもしれませんが、それでも引数としては1つです）

#### 寄り道：JavaScriptでカリー化された関数を見てみよう

JavaScript で同じ関数をカリー化して利用することも出来ます (この例は Google Chrome の開発者向けツールのコンソールに直接入力しました).

```javascript
> var fc = a => b => x => a * x + b
< undefined
> fc
< a => b => x => a * x + b
```

もしかしたら、伝統的な ```function``` 予約語を使って定義したバージョンの方が理解しやすいかもしれません。

```javascript
var fc3 = function(a) { 
    return function(b) { 
        return function(x) { 
            return a * x + b 
            } 
        } 
    }
```

ところで、引数 ```a``` の値が、最内部の関数の本体からもアクセスできるのはクロージャー（closure) のおかげです。引数 ```b``` に関しても同じです。そうやって式 ```a * x + b``` に ```a``` と ```b``` の値が渡されます。クロージャは JavaScript でカリー化を可能にさせる機能なのです。

２つの関数 ```fc``` と ```fc3``` は振る舞いとしては全く同じですし、両方ともカリー化されているので、呼び出し方法も同じです。（記述の仕方としては ```fc``` の方がずっと簡単ですね）

```javascript
fc(2)(5)(3)
11
fc3(2)(5)(3)
11
```

カリー化された関数は引数を１つしかとらないので、それぞれの入れ子になった関数に対しても同様に引数は１つしか渡しません。そのため ```(2)```, ```(3)```, そして最後に ```(3)``` という形で引数を別々に渡しています。

では、JavaScript の世界を離れて、Scala の世界に戻りましょう。

Scala でのカリー化された関数の呼び出しもこのようになります。

```scala
scala> fc(2)(5)(3)
res99: Int = 11
```

特に驚きの要素はありませんね。記述の仕方としては、引数として１つ渡すというのは変わらないので、JavaScriptの場合と全く同じです。

#### 複数の引数を取る関数をカリー化する

すでに存在する複数の引数を取る関数を、簡単にカリー化できるとしたら便利ですよね。幸運にも Scala にはビルトインで簡単にカリー化できる機能があります。

以下では、３つの引数を取る関数 ```f``` のカリー化されたバージョンを ```fc``` という名前を付けて定義しています。

```scala
scala> def fc = (f _).curried
fc: Int => (Int => (Int => Int))

scala> fc(2)(5)(3)
res103: Int = 11
```

```(f _)``` の部分は関数 ```f``` そのものを表していて、関数を呼び出さないようにしています。アンダースコアを指定することでインタープリターが関数呼び出しを実行することを防いでいます。```curried``` というメンバーからカリー化された関数を得ることが出来ます。

## カリー化された関数から特殊化(specialized)された関数を作る

ｋの段階は、カリー化された関数からどのように特殊化された関数を作るか、という話題はもう必要ないでしょう。もうすでに何度も例を見てきましたから。

以下では、引数```a``` と ```b``` に値を渡し（ただし```x```に関しては未知のままとする）、新たに ```fc4``` という名前の特殊化された関数を作ります。 関数```fc4``` を呼び出す際は、引数 ```x``` に対する値を渡すだけです。

```scala
scala> def fc4 = fc(2)(5)
fc4: Int => Int

scala> fc4(3)
res104: Int = 11
```

## カリー化された関数で関数合成をする

### ```andThen``` メソッドと```compose``` メソッドのおさらい

すでに ```andThen``` メソッドを使って関数を合成するやり方を見てきましたが、```compose``` メソッドを使って同じように関数を合成できます。このセクションでの例は```compose``` メソッドを利用するので、その使い方をまず確認しておきましょう。

以下のような２つの関数があったとします。

```scala
scala> def inc(x:Int) = x + 1
scala> def mulBy2(x:Int) = x * 2
```

この二つの関数を ```mulBy2(inc(x))``` ような関係にしたいとします。例えば、 ```x=1``` の時 ```4``` が結果になるように、または ```x=2``` の時 ```6``` が結果になるような組み合わせです。

```compose``` メソッドを使うと、このように関数を合成できます 

```scala
scala> def c1 = (mulBy2 _).compose(inc _)
```

関数 ```c1``` を呼び出して、正しく合成されているか確認します。

```scala
scala> c1(1)
res206: Int = 4

scala> c1(2)
res207: Int = 6
```

同様に逆の関係 ```inc(mulBy2(x))``` にするには以下のように組み合わせます（この場合 ```x=1``` なら結果は ```3``` で、```x=2``` なら結果は ```5``` になるはずです）

```scala
scala> def c1 = (inc _).compose(mulBy2 _)
```
つまりコンポーズする関数は*コンポーズされた関数の出力*をその入力にとるわけです。

以下、```compose``` メソッドを利用して、先に定義したカリー化された関数 ```fc``` （下参照）を改造していきます。

```scala
def fc(a:Int)(b:Int)(x:Int) = a * x + b
```

ではちょっとシナリオを設定しましょう。この関数 ```fc```は引数を3つとるわけですが、 ```a``` と ```b``` 関して、以下のようなパラメータ検証をすることになったと仮定します。

- 引数```a``` の値は常に絶対値に変換してのち、```fc``` に渡さなくはならない (ヒント：Math.abs ライブラリ関数が使えそうです)
- 引数```b``` の値は5以下でなくてはならない (ヒント: Math.min ライブラリ関数が使えそうです)

これらを式として表すとこうなります：

```scala
Math.abs _
def atMost5 = Math.min(5, (_:Int)) // ヘルパー関数を作成しました
```

ヘルパー関数 ```atMost5``` がきちんと動くかテストしておきましょう（高階関数 ```map``` はこんな時にも便利です）

```scala
scala> (1 to 10) map(atMost5)
res8: IndexedSeq[Int] = Vector(1, 2, 3, 4, 5, 5, 5, 5, 5, 5)
```

入力が５より大きい時でも出力は５になっています。

では、引数 ```a``` と ```b``` の値それぞれに事前処理を施しつつ、関数 ```fc```と合成してみましょう。まずは引数 ```a``` から初めて、段階を追ってみていきます。

```scala
scala> (fc _).compose(Math.abs _)
res10: Int => (Int => (Int => Int)) = // 右辺は省略しました
```

この合成のパターンは ```fc(Math.abs(a))``` という関係に２つの関数を結び付けます。ここで気を付けなくてはいけないのは、この合成された関数はあくまでも *まだ３つの引数を取る関数* であるということです。決して *２つの引数を取る関数*にはなっていません - それは部分適用の話です。これは関数合成の話であって、どの引数にもいまだ値は適用されていません。重要な点は、後の実行時に、引数```a``` として渡される値は、合成された関数 ```Math.abs``` を*通って* ```fc``` に到達する、ということです。

次に、２つ目の引数 ```b``` に対する事前処理（最大で値は```5```になるように強制する）を行うように関数を合成しましょう。この場合、```b``` へ値がヘルパー関数 ```atMost5``` を*通り抜けていく* ように構成しなければなりません。つまり、１つ目の引数をなんとかスキップしなくてはならないのです。

```scala
scala> (fc _).compose(Math.abs _)(_:Int).compose(atMost5)
```

注目してもらいたいのは ```(fc _).compose(Math.abs _)``` の部分のすぐあとに来る ```(_:Int)``` の部分です。これは１つめの引数は「未知のまま」としてスキップしていて、２つ目の引数に対して - 言い換えると「２つ目の引数を受け取る、１つしか引数を受け取らないカレー化された関数」に対して - ```compose``` メソッドを使って ```atMost5``` と結び付けているのです。

では、この合成された関数に名前をつけて新し関数として定義しましょう。

```scala
scala> def fcx = (fc _).compose(Math.abs _)(_:Int).compose(atMost5)
fcx: Int => (Int => (Int => Int))
```

合成関数 ```fcx``` はいまだにトータルで引数を３つ取ることは変わりません（```=>``` 記号の左側が引数ですから、数えるとInt が３つあります）。まだ引数の値はひとつも渡していないので、ベースの関数 ```fc``` は引数を３つ必要とします。 (ヘルパー関数 ```atMost5``` に対して値```5```を部分適用して固定しましたが、これは ```fc``` 関数に対する部分適用ではありません)。

ベースになったカリー化された関数 ```fc``` を一切変更することなく、２つの引数が事前処理されるように構成することができました。

同様のことを命令型プログラミングでやるとどのようになるでしょう。```fc``` 関数本体を変更するやり方もあるでしょうし、呼び出しの際に、インラインで事前処理の関数を呼び出す方法もあるでしょうし、一種の関数合成として別名を与えることも出来ます。ただ、はたしてそれが好ましい方法なのかどうかは議論の余地があるでしょう。

関数型プログラミング、さらにはカリー化と合成関数は、このような特殊化(specialization)が普通に起こるという状況で、その柔軟性を発揮することが出来ます。

では実際にテストしてみましょう。テストケースは３つ用意します。

- ```fcx(2)(5)(1)``` は、 ```2 * 1 + 5``` なので結果は ```7```になる。
- ```fcx(-2)(5)(1)``` は、まず ```-2``` が ```Math.abs``` によって ```2``` に変換され、```2 * 1 + 5``` となるので、結果は ```7```になる。
- ```fcx(2)(8)(1)``` は、まず ```b=8``` が ```atMost5``` によって強制的に ```b=5``` になり、```2 * 1 + 5```となるので、結果は ```7```になる。 

テスト結果：

```scala
scala> fcx(2)(5)(1)  
res27: Int = 7       
                     
scala> fcx(-2)(5)(1) 
res28: Int = 7       
                     
scala> fcx(-2)(8)(1) 
res29: Int = 7       
```

期待通りの結果になりました。

## カリー化された関数の利点とは？

オンラインでこの答えを探すと（少なくとも英語圏では）あまり統一の取れた見解はでてきませんでした（もちろん、調査する私の力不足も含めての結果ですが・・・）。多く見られるのが、部分適用の利点をカリー化の利点としてしまっている主張です。ですが、この記事でも解説した通り、部分適用はカリー化を必要しないので、部分適用の効果をカリー化の効果とするのは正しくありません。

ですが、カリー化（とカリー化された関数）の利点を、部分適用とは別けて解説しているブログをとりあえずひとつ見つけました。 [article by Iven Marquardt](https://medium.com/@iquardt/currying-the-underestimated-concept-in-javascript-c95d9a823fc6)。この記事によると、カリー化の利点は 

> *abstracts the variations of number of arguments* （引数の数の違い・バリエーションを抽象化する）

とあります。これはなるほどと納得できるものです。関数を合成しようとして、よく遭遇する「壁」として、引数と戻り値の型の他に、引数の数があります。うまく合成してパイプライン、またはツリー構造に構成したくとも、既存の関数が複数の引数を取るためにうまくいかなかったりします。ですが、カリー化は「引数は常にひとつ」という状態を保証するので、粒がそろうことで関数合成がしやすくなります。

関数型プログラミング入門者の私にとって、もっとも納得いく説明はこのようなものでした。