関数型プログラミングとはいったい……うごごご!
とりあえず、どうすべきなのよ
関数型プログラミング使いとは
- コードの参照透過性の為に自らに重い枷を課し世俗の快楽を捨て去り突き進む孤高の戦士(変態)
- スタックを食い潰したその険しい道程の果てにモジュール性の高い、テスト明快なコードが見えてくる
関数型プログラミングが可能な言語
- 様々な論争がある
- ファーストクラスオブジェクトとしての関数、参照透過性の確保が可能な言語であれば関数プログラミングは可能であろう、との事
関数型 <=> 命令型
- 命令形モデルに基づいたプログラミング言語が長い間隆盛を誇ってきた
- ハードウェアの構造に近い命令型モデルの方が実現しやすく関数型モデルではメモリやコンパイルの効率的な管理が難しかった
- しかし処理能力の向上、メモリの巨大化等、計算リソースが豊かになるにつれて実装上の問題は殆ど解決
- 関数型モデルの優れた性質が再び脚光を浴びてきている
関数型のメリット(らしい
1 置換ルール
当然のように思えるが
var hage = (a) => {
return a + 2;
}
// hage(1)
// => a + 2
// => 1 + 2
// => 3
// 関数内部での a => 1 の置換が行われる(らしい・・これって凄いのか・・?
2 計算の過程の明示化
var add = (a, b) => {
if (b === 0) {
return a;
}
else {
add(a + 1, b - 1);
}
}
// add(1, 3)
// => add(2, 2)
// => add(3, 1)
// => add(4, 0)
// どのように計算が進んでいくか捉えやすい(らしい
3 高階関数
- 関数を引数として渡せる
- 関数を戻り値として返せる
- 巨大化するソフトウェアを管理する際に強力な武器になる(らしい
変数
バインド・束縛
値を変数に対応付ける事
// これ
var moko = "hage";
代入
変数の内容を更新する事。代入は変数の参照透過性を破壊する
var moko = "hage";
// これ
moko = "husa";
参照透過性
- 何かを評価した際、入力が同じなら、必ず同じ値が帰ってくるという性質
- 高いモジュール性を持つ
- 数学的な等式が成立する(テストしやすそう!
参照透過性を破壊するもの
可変データ(配列、ハッシュ、オブジェクト等 | 値の参照透過性を破壊する |
代入 | 変数の参照透過性を破壊する |
副作用 | 関数の参照透過性を破壊する |
参照透過性 [値]
プリミティブなリテラル値は参照透過性が保たれてる!すごーい!
1 === 1;
=> true
参照透過性 [変数]
当然のように思えるが
var hage = 1; // hageに1をバインド
hage === 1;
=> true
// この時点でhageの参照透過性は保たれている
hage = 2; // hageに2を代入
hage === 1;
=> false
// hageは 1 と言ったら、そのコンテキスト内では必ず 1 でいてほしいの!
// 代入は参照透過性を破壊する
// 同じ理由で破壊的メソッド(関数)も使用禁止
- 代入は変数が指す値に曖昧さをもたらす
- コードが(関数が)状態を持つ(持ってしまう)と言うこと
参照透過性 [関数]
var x = 0;
var add = () => {
x = x + 1; // これはバインドではない 代入である
return x;
}
add() === add()
=> false
副作用
代入の有無とは関係なく、もともと参照透過性のない関数がある
Date.new() | 外部環境 |
Math.random() | 外部環境 |
console.log() | 画面 |
File.read() | 外部ストレージ |
DB.where() | 外部ストレージ |
Net.get() | ネットワーク |
- 上記のようなものは処理系のコンテキスト管理外に位置するものである
- 値を返す以外の副次的な機能を副作用と呼ぶ
- その関数を 副作用のある関数 と呼ぶ (あれ?副作用を持つ関数?だっけ?)
- 副作用のある関数を呼び出す関数には副作用がある => 参照透過性がない
値の参照透過性を保証する
可変なデータ型を使わなきゃいいじゃない!(配列、ハッシュ、オブジェクト型等)
変数の参照透過性を保証する
- 変数は代入により参照透過性を失う => 代入を使わなきゃいいじゃない!
- 代入を使わないという事は for while 系のループが使えないということであるが、その辺りで reduce, map, 再帰 が脚光を浴びる
関数の参照透過性を保証する
- 全ての副作用を無くしてしまえばい・・いや、そういう訳にはいかない。ファイルやDBにはアクセスしたいし、画面には結果を表示しなければならない
- 副作用はただ排除するのではなく、副作用を持つ部分をきちんと分離する
- 副作用の無い関数は副作用のある関数から呼ばれても問題ない
- 副作用のある関数を副作用のない関数から呼び出してはならない。副作用が伝染るんです
- 参照透過性を持つ関数を 純粋な関数 とも言う
このmoko関数は今日と明日では違う結果を返す。つまり参照透過性がない。純粋な関数でない
var moko = () => {
return new Date().getDate();
}
moko()
=> 1
このmoko関数は副作用を持たなくなり、参照透過性がある、純粋な関数になった
var moko = (date_object) => {
return date_object.getDate();
}
moko(new Date());
=> 1
もう一つ例
var moko = (hage) => {
var result = (hage === true) ? "hage!" : "mada!";
console.log(result)
}
moko(true);
=> hage!
ではなく、
var moko = (hage, console_log) => {
var result = (hage === true) ? "hage!" : "mada!";
console_log(result)
}
moko(true, console.log);
=> hage!
参照透過性・・やっぱよくわからない たぶん、みんな判ってない
ぐぐってそれっぽいものを並べる
- 式、値、関数と、その式の指す実体(評価した結果)との間の「参照」関係が、(文脈や副作用といったものから独立なので)至って明瞭(transparent)である」ことを「参照透過である」とするようです。変数への再代入ができないことや副作用云々といった議論は、そのための、特定のプログラミング言語における必要条件でしかありません
- 翻訳の際に、“referential transparency” からの直訳で「参照透過性」としてしまったのだと思われますが、英語の “transparent” には、物質的な「透明な」から派生して、「明瞭な」とか「ありのままであきらかな」といった意味があります。日本語としては、「参照明瞭性」とでもした方が本来の意味に近く、理解しやすかったのではないかと思います
- コンテキスト内のある語を、文全体の意味を変えることなく、「その語が指し示すところの実体」で置換することが可能な時、そのコンテキストは「参照透過である」と言う
ダメだ わからん
参照透過性を理解したければ哲学者とか理論学者になるしかない
- 変数への代入はするな、変数へのバインドはおkだ
- 破壊的メソッド(関数)は使うな
- 副作用のあるメソッド(関数)は呼び出すな
- reduce と map と 再帰 と仲良くしろ
もうこれだけでいいんじゃないだろうか
「お前、参照透過性まだ理解してないの?」とか言う奴がいたら
- そいつは関数型世界の自称トップランナーだ
- 騙されるな