文系卒のクソザコWEB系エンジニアが関数型言語に入門した結果をまとめた

私は文系卒のクソザコWEB系エンジニア[^0]です。私と同じく文系出身で「関数型言語何もわからん……」となった方のために、私が関数型言語を学ぶ中で疑問に感じた内容にフォーカスしてまとめることにしました。関数型言語初心者ですので誤った記載があるかもしれませんが、その際はコメント欄等で温かなマサカリを投げつけてくだされば嬉しいです。

[^0]: 数学については圏論はおろか数2Bに毛が生えた所までしか知らない身分なのですが、並列処理が求められる場面でScalaを利用することになったり、高速性が求められる場面でRustを学ぶ機会があったりして、関数型言語あるいは関数型プログラミングという概念に興味を持ちました。

## そもそも「関数型言語」「関数型プログラミング」って？

いろいろな記事を当たってみましたが、[こちらの記事](https://qiita.com/hiruberuto/items/26a813ab2b188ca39019)の

> 「関数型プログラミングの定義」などという存在しないものを誰かにねだらないほうがいいと思います。

という表現が一番しっくりときました。つまり、**よくわかりません**。

## なるほど。では、どんな言語が「関数型言語」を名乗っているの？

確かに、具体例を挙げることなら簡単です。以下の表の通りです[^1]：

| 大分類       | 小分類             | 例           |
|-------------|--------------------|--------------|
| 関数型言語   | 純粋関数型言語      | Haskell, Elm |
|             | 非純粋関数型言語    | Scala, F#    |
| 手続き型言語 | 命令型言語          | COBOL, C     |
|             | オブジェクト指向言語 | C++, Java    |

[^1]: なお、**オブジェクト指向言語にも（非純粋な）関数型プログラミングの機能を含んでいる言語は多数存在しますし、非手続き型言語には関数型言語以外の枠組みも存在します**が、ここでは簡単のために触れないことにします。


## Scala は関数型言語なの？

**非純粋関数型言語の側面を含むマルチパラダイム言語**です。Java より並列処理が得意ということもあり、Gatling という負荷試験ツールに採用されたりしています。[^2] 一方既存の Java 資産を活用したり、手続き型の書き方をした方がいいと思われる場合には、手続き型のような書き方をすることもできます。

[^2]: Scalaの「並列コレクション」を利用するとコレクションに対する処理を簡単にマルチスレッド化することができます。また、AkkaというScala製のアクタベースの並列実行ライブラリが組み込まれています（かつてはオリジナルのアクタベース並列実行機能を持っていましたが、現在は非推奨になっています）。

## Rust は関数型言語なの？

**非純粋関数型言語の側面を含むマルチパラダイム言語**です。Rust はデータの所有権と借用の概念を持っており、データの競合をコンパイル時に防ぐことができます。また、変数はデフォルトでイミュータブル（不変）になっているため、副作用（後述）の発生が局所化されています。

## 関数型言語の特徴として挙げられがちなポイントは？

主観ですが、以下の二点について言及されることが多かったように思います：

1. 参照透過性と副作用の抑制
2. 高階関数とカリー化・部分適用

### 参照透過性って？ 副作用がないと何が嬉しいの？

**参照透過性**とは、**変数の値がイミュータブル（不変）である性質**のことです。変数の示す値が途中で変化せず、いつ参照しても値が同じであるため、このように呼ばれます。変数がイミュータブルであると、コード全体の見通しが良くなり、ヒューマンエラーの発生を抑止することができます[^3]。**参照透過である変数は、その実体と置換しても意味が変化しません。**

[^3]: このあたりは関数型言語特有というより変数のミュータビリティに関する議論です。JavaScriptでもvarよりlet、letよりconstを使っていこうという風潮がありますよね。

参照透過性は**戻り値が引数にのみ依存して決まる関数**にも付与されます。つまり、環境[^4]からの影響を受けず、環境に対して影響を与えない関数です。このとき、関数に同じ引数を与えると、必ず同じ戻り値を返します。**参照透過である関数は、引数が同じであれば、その戻り値に置換しても意味が変化しません。**

[^4]: スコープ外の変数など

**副作用**とは、**その処理の実行前後で環境に外部から観測可能な変化を与えてしまう**ことです。たとえばミュータブル（可変）な変数へ値を再代入する操作は外部から観測可能な変化ですので、副作用を発生させる操作であると言えるでしょう。このような副作用を伴う処理にはバグを埋め込みやすいという意見があります[^5]。

[^5]: あなたがJavaScript経験者ならミュータブルなグローバル変数の変更・参照タイミングにまつわる失敗経験を思い浮かべて頂くととわかりやすいかもしれません。変数への再代入を許可すると、そのようなデータの競合に伴うトラブルの原因になるかもしれません。俺のサイドエフェクトもそう言っています。

また、副作用が無いコードには副作用があるコードと比較して**処理の並列化が行いやすい**というメリットがあります。環境に対する変化を発生させないため、マルチスレッド化を施してもデータ競合関連のトラブル（デッドロック等）を発生させにくいのです。

副作用を発生させない（状態を持たない、安全な）関数を**純粋関数**と呼び、そうでない（状態を持つ、安全でない）関数を**非純粋関数**と呼びます。また、すべての変数がイミュータブルな関数型言語を**純粋関数型言語**と呼び、そうでないものを**非純粋関数型言語**と呼びます[^6]。

[^6]: 純粋関数型言語でもI/Oのようにアクションが求められる場面はありますが、この場合はモナドを利用します。

### 高階関数って？ カリー化・部分適用って？

**高階関数**とは**引数として関数を受け取ったり、戻り値として関数を返したりする関数**のことです。JavaScript だと `Array.prototype.forEach()` あたりが相当します。

**カリー化**とは、**二つ以上の引数を取る関数を、一つの引数だけを取る関数の組み合わせに書き換える**ことです。次に挙げる例のように、高階関数を用いて実現されます：

```javascript:before
// 2つの数字を引数に取り、双方を比べて大きな方を返却する関数：
const max = (x, y) => {
    return x > y ? x : y
}

max(1, 2) // 2
```

```javascript:after
// カリー化を施します：
const max = x => {
    return y => {
        return x > y ? x : y
    }
}

// あるいはもっと単純に：
const max = x => y =>{
    return x > y ? x : y
}

max(1)(2) // 2
```

**部分適用**とは、**複数の引数を受け取る関数を、より少ない数の引数を受け取る関数に変換する**ことです。引数の値を固定していると言っても良いでしょう。カリー化が施された関数は、簡単に部分適用を行うことができます：

```javascript
// 「1つの数字を引数に取り、1と比べて大きな方を返却する関数」に変換します：
const max1 = max(1)

max1(2) // 2
```

部分適用により**関数のインターフェース（パラメータの個数）をカスタマイズできるため、関数⇔関数の情報の受け渡しをスムーズに行えます**。関数型言語ではシェルにおけるパイプラインのような感覚で関数を合成するため、部分適用によるインターフェースの変換が便利な手法になるのです[^7]。

[^7]: JavaScriptでも[パイプライン演算子](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Pipeline_operator)の導入が検討されています（現在 Stage 1 Draft です）。また、[カリー化を用いてお手軽にパイプラインを実現する方法](https://qiita.com/nagtkk/items/5c54ec418c1c71fa491a)についての記事もあります。ド文系なので関数合成について具体的な例で理解できて助かりました。

## なんで「代入する（assign）」じゃなくて「束縛する（bind）」っていうの？

関数型言語のドキュメントでは「変数に値を代入する」ではなく「変数に実体[^8]を束縛する」と表現されていることがあります。これは変数がイミュータブルであり、宣言と同時に特定の実体で固定されるためです。

[^8]: ここで値ではなく実体と表現しているのは、実体として値以外のもの（式など）を束縛できる場合もあるためです。式が遅延評価される言語の場合、宣言と値の固定が同時である保証はありません。


## Vue.js の関数型コンポーネントと関係あるの？

Vue.js のコンポーネントは `data` を持っています。つまり、状態を保持しているということです。一方、関数型コンポーネントには `data` がありません。自分自身では状態を持たず、親コンポーネントの状態を `props` で渡すことによって動作します。つまり関数型コンポーネントは引数として「状態」を受け取り、戻り値として `VNode` を返却する「ただの関数（ just functions ）」であると言えます。

関数型コンポーネントの主なメリットは描画コストの低減（＝レンダリング高速化）ですが、状態が親コンポーネント側に集中する設計になることで、結果的にコンポーネント内の見通しがよくなる場合もあります。なるほど、参照透過性に関する特長と通じるものがありますね。

## Redux や Vuex と関係あるの？

**Redux と Vuex は純粋関数型言語である Elm から大きな影響を受けています**。たとえば、Redux の Reducer や Vuex の Mutation は純粋関数でなければならないとされています。これは Reducer（変化させる + er）という名前が示すように、引数として受け取った state を変化させて返却する以外のことをしてはならないということです。

ちなみに、Redux の middleware の定義にはカリー化を用います：

```javascript
const middleware = store => next => action => {
    next(action);
}
```

## 関数型言語は銀の弾丸なの？

**関数型言語は銀の弾丸ではありません**が、関数型プログラミングの考え方が有用な場面はあると考えます。上の二項目がまさしくその例でしょう。関数型プログラミングの特性を十全に活かすことはできないかもしれませんが、その知見を活用することは難しくありません。

## オブジェクト指向のほうがわかりやすくない？

まず、オブジェクト指向と関数型プログラミングそのものは互いに相反しあうものではないという前提を共有したいと思います。そうでなければ、Scala や Rust のようなマルチパラダイム言語は存在し得ないし、Java にラムダ式が取り入れられることもなかったでしょう。Redux や Vuex に Elm の思想が取り入れられることも無かったかもしれません。しかし、関数型プログラミングの特性を活かしきるためには純粋関数型言語を選ばざるを得ず、そういう意味で純粋関数型言語とオブジェクト指向言語は対立軸にあります。

私はオブジェクト指向と西洋哲学には共通した部分があると認識しています[^9]。プラトンの「イデア論」やアリストテレスの「形相と質料」などは特にわかりやすい例です。すなわち、人間が世界を理解するために生み出され、古代から連綿と受け継がれてきた思考方法を、これもまた世界の一部であるプログラミングに適用しているわけですから、わかりやすくて当然です。また、プログラミング初学者でも義務教育等を通じて西洋哲学にはどっぷり影響を受けているはずであり、なじみやすい概念もまたオブジェクト指向であると考えられます。

[^9]: オブジェクト指向と西洋哲学との関係性については[河合昭男先生の著作](http://www1.u-netsurf.ne.jp/~Kawai/)が非常に参考になります。

……というのは私が文系人間だからかもしれませんが。





## 参考リンク

<details><summary>参考文献が多すぎてどちらが本文かわからなくなったので折り畳みました。インターネットは偉大。</summary><div>

### 関数型言語について

* [関数型言語、聞いたことあるけど結局何なの？](https://tech.enigmo.co.jp/entry/2018/12/24/112917)
* [関数型プログラミングとは結局何なのか](https://blog.kokuyouwind.com/archives/808/)
* [関数型言語のウソとホント](https://qiita.com/hiruberuto/items/26a813ab2b188ca39019)
* [なぜHaskellを学ぶと良いか](https://qiita.com/arowM/items/0305d4f439752f285438)

### Rust について

* [Rustは何が新しいのか（基本的な言語機能の紹介）](https://imoz.jp/note/rust-functions.html)

### Scala について

* [とりあえず Better Java として Scala を使ってほしい](https://qiita.com/soycoffee/items/9e4336d61add941ca805)
* [スケーラブルで関数型でオブジェクト指向なScala入門](https://www.atmarkit.co.jp/ait/series/889/)
* [Java と Scala での並行処理の基礎](https://www.ibm.com/developerworks/jp/java/library/j-jvmc1/)

### 参照透過性と副作用について

* [純粋関数型JavaScriptのつくりかた](https://qiita.com/hiruberuto/items/810ecdff0c1674d1a74e)
* [純粋関数型言語と参照透過性](https://qiita.com/Yametaro/items/1de3c2b76b8a4dc2d30d)
* [「参照透過である」とは、何から何への参照がどういう条件を満たすことを言うのか](https://qiita.com/knaka/items/b18134d9d11b51da4e2e)
* [プログラミングで言う「副作用」とは何ですか？副作用があるのは良くないとかHaskellなどの関数型言語は「IOモナド (IO monad) 」で副作用なしにプログラムできると聞きますがそれは何ですか？](https://jp.quora.com/%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E3%81%A7%E8%A8%80%E3%81%86-%E5%89%AF%E4%BD%9C%E7%94%A8-%E3%81%A8%E3%81%AF%E4%BD%95%E3%81%A7%E3%81%99%E3%81%8B-%E5%89%AF%E4%BD%9C%E7%94%A8%E3%81%8C?)

### カリー化と部分適用について

* [食べられないほうのカリー化入門](https://qiita.com/KDKTN/items/6a27c0e8efa66b1f7799)
* [高階関数、カリー化、部分適用](https://qiita.com/nouka/items/d9f29db7b6a69baa650a)
* [カリー化と部分適用](https://www.ibm.com/developerworks/jp/java/library/j-jn9/index.html)
* [curry化と部分適用の違いを自分なりにまとめてみる](https://qiita.com/hirohero/items/1af33891aa9db5092069)
* [部分適用っておいしいの? on Arduino](https://qiita.com/ttatsf/items/8f092ac3eba5373042da)
* [JavaScript/TypeScript でお手軽パイプライン](https://qiita.com/nagtkk/items/5c54ec418c1c71fa491a)

### 束縛という用語について

* [Haskellの変数には値を代入しません、束縛します。](https://qiita.com/lotz/items/aca1d179c14d4dca5099)
* [「名前の束縛」という名の束縛](http://fumieval.hatenablog.com/entry/2018/10/31/150056)

### Vue.js について

* [関数型コンポーネント](https://jp.vuejs.org/v2/guide/render-function.html)
* [関数型コンポーネントってなに？](https://python.ms/functional-component/)
* [プロダクトのパフォーマンスを改善するためにVue.jsの関数型コンポーネントやpropsに関する施策を行った話](https://tech.smartcamp.co.jp/entry/vuejs-performance-improvement)

### Redux / Vuex について

* [Redux 入門 〜Reduxの基礎を理解する〜](https://qiita.com/soarflat/items/bd319695d156654bbe86)
* [Redux 基礎：Middleware 編](https://qiita.com/yasuhiro-okada-aktsk/items/1fe3df267a6034d970c1)
* [関数型プログラミングとRedux | Reduxが必要なとき/不要なとき（翻訳）](https://techracho.bpsinc.jp/hachi8833/2018_03_13/53183)
* [Elmの型で読むReduxやVuexのアーキテクチャ](https://qiita.com/lambda_funtaro/items/df89a8531f861e3f42a6)
* [Reduxが分からない人のためにReduxを概念から説明してみる](https://qiita.com/tkow/items/9da7062f9bfa99e848c3#reducer)

### オブジェクト指向と西洋哲学について

* [情報システム学会（ISSJ）メールマガジン連載　「オブジェクト指向と哲学」](http://www1.u-netsurf.ne.jp/~Kawai/papersISSJ.html)
* [オブジェクト指向の世界](https://www.itmedia.co.jp/keywords/object_oriented_world.html)
* [「アラン・ケイの」オブジェクト指向とは何だったか？元哲学者のエンジニアがまとめてみた](https://qiita.com/ukyo-su/items/8c861f114809a96d1378)

</div></details>