どうも、純粋枠ののどかはです。
にわか関数型言語オタクなので、好き勝手に純粋関数やモナド則をもった関数型言語の歴史と意味合いを解説していきます。
数学的な説明や正しい説明ではなく、簡易的な解説です。
詳しい解説を最後に書きますが、持論しか展開されないので読まなくて良いです。
関数型言語とは
ここで、関数型言語を定義することは控えたいので、割愛します。
オブジェクト指向と対を成すプログラミング言語の分類の一つなんだという納得で問題ないです。
定義はしないので、簡単な説明はしておきます。
これはあくまでこの記事を読む上で齟齬を発生させないための記述であり、厳密ではないことに注意してください。
オブジェクト指向言語はプログラムに現われる特殊なもの全てをオブジェクトだと考えます
対極的に
関数型言語はプログラムに現われる特殊なもの全てを関数で実装出来ると考えます
この2つのニュアンスが大分異なっていることには注意してください
純粋関数とは
今回の記事では関数型言語に焦点を当てる訳なので、純粋関数というものについて説明します。
広義としての純粋関数は副作用のない関数のことです。
羃等性をもっていると言い換えても良いかも知れませんし、ちょっと問題あるかも知れません。
これはそのプログラミング言語の純粋関数の実装具合によります。
どうしてプログラミング言語ごとに違いがあるかのように話すかと言うとそもそもプログラミング言語は評価順序というものが実はあるからです。
プログラムが読み取られた時の動作がそもそも、プログラミング言語固有なことがあるからです。
さて、恐らくもうこんがらがってきたと思うので、結論だけ書くとスコープの外にある変数を一切関わらせない関数は純粋関数なことが多いです。
以下のようなやつです。(PHPで書いていきます
function pure(int $num)
{
return $num + 1;
}
え?みたいな気持ちになりますよね
そうです、普通の関数です。
一応これは純粋関数です。
どうしてかを考えてみましょう。
これに引数が1を渡されたことを考えてみましょう。
はい、2ですね。
そういうのを純粋関数って考えて良いです。
入力が分かれば結果が分かるもの。
人間にとっても分かりやすいですね。
ではこれはどうでしょうか。
function read(string $world)
{
return "hello" . readline() . $world;
}
これは純粋関数ではないです。
どうして??と思うでしょう。
この関数が呼びだされた時、ユーザーの入力がなければ結果が分かりません。
ですので"Hello"と$worldとして渡された文字列があるのは分かりますが、この関数だけでは結果が分かりません。
それならさっきのpure関数だって分からないじゃないか?となるかも知れません。
ですがpure関数は結果がn+1であることは分かります。
予測出来る訳です。
純粋関数とは値が決まった瞬間に結果が分かるもののことです。
テストとかでプログラムの挙動を値で保証しているみたいですね。
自分は純粋関数のような性質を
プログラムを式として保証出来るもの
と考えています。
きっとこれは賛否両論を生んでしまうので、さっさと次に行きます。
モナド則とは
次にモナド則の説明をします。
モナド則はプログラムが同等の性質を示す状態を保ち続けたまま変換できる操作の集まりのことです。
最初から意味が分からないですね。
めっちゃ簡単な説明は
プログラムが同じ処理だと言える書き換えはどこまでなのか
っていう規則の集まりです。
はい、今回の入門は正直これ以上うまく語れませんでした。
申し訳ないです。
こっから先はただやばいオタクが語ってるだけです。
こっから分かんないやつ
順番に説明します。
ただし、PHPではモナド則を正確に示すのはまあ厳しいので、こんな感じと思ってもらえればと思います。
以下の関数があります。
function monad1(int $x)
{
return $x + 1;
}
echo monad1(1);
この関数は以下と同じ性質です。
// 動かないけど厳密な書き方
echo (function($x){$x})(1) + 1;
// 動くコードの書き方だと以下
echo (function($x){return $x;})(1) + 1;
これをモナド則①と呼び
returnが関数に関して(厳密にはモナドに関して)左単位元になっていることを要請します
誤解を招く言い方だとreturnがあろうとなかろうと良いということです。(PHPでは関数にreturnなしでは動かせませんでしたが…)
次に以下の関数があります。
// 厳密な書き方
echo (function ($x){return 1 + (function($x){$x})($x);})(1);
// 動くコード
echo (function ($x){return 1 + (function($x){return $x;})($x);})(1);
この関数は以下と同じ性質です。
// 厳密なコード
echo (function ($x){(function($x){return 1 + $x;})($x);})(1);
// 動くコード
echo (function ($x){return (function($x){return 1 + $x;})($x);})(1);
これをモナド則②と呼び
returnが関数に関して(厳密にはモナドに関して)右単位元になっていることを要請します
他の人の分かりやすい言い方だと、returnがどこにあっても良いということです。
最後に以下の関数を見てください。
function monad3($x)
{
function($y)
{
function($z)
{
return $x + $y + $z;
}
}
}
この関数は以下と同じです
function monad3($x)
{
function($y, $z)
{
return $x + $y + $z;
}
}
これをモナド則③と呼び
結合則と呼びます。
これだけ、めっちゃ分かりやすいですね。
関数が合成出来るということです。(厳密じゃない言い方をすれば
以上の操作はどこでどうやっても処理の内容が変更されません。
羃等性がある訳ですね。
とっても嬉しいですね。
結論
羃等性を保証したプログラミングの規則として純粋関数とモナド則に縛られた世界を生きれば決して型がいきなり変わるとか、意味分かんない歴史的経緯に振り回されて最悪なコーディング体験に沼ることなく、健全で潔白で純粋なコーディング体験が出来るということです。
素晴しいですね。
はい、実用性はあるのかって?
IOは副作用なのでユーザーの入力が一切必要としない要件を定義して頂ければ、この素晴しい体験を御提供できます。
…まあIOモナドという概念があるので、頑張れば出来ますが
はい
実用的でないことは分かってもらえると思います。
自分はこれらをデータに羃等性を持たせたいなら静的型システム、処理に羃等性を持たせたいなら純粋関数やモナド則、だと思ってます。
個人の感想です。
関数型はこれを究極とも言っているし、実用的じゃないからもっと緩い方が良いと思ってる人も居るし賛否両論です。(並列処理とかでは重宝します
皆さんは適度にオブジェクト指向や関数型を使っていきましょう。
それでは良きプログラミングライフを。
関数型言語オタクののどかはでした。