純粋関数型言語と参照透過性


登場人物

ワイ・・・ワイ。
ハスケル子・・・インターンの中学生。こちらの記事を参照。

素直になったワイ

ワイ「なぁ、ハスケル子ちゃん」
ワイ「ちょっと教えてほしいことがあるんやけど」

ハスケル子「なんですか」

ワイ「ハスケル子ちゃんの好きなHaskellっていうのは」
ワイ「純粋関数型言語ってやつやんな?」

ハスケル子「そうですね」

ワイ「なんというか、よう分からへんねんけど」
ワイ「純粋関数型言語を使うと何が嬉しいんか、ワイに教えてくれへん?」

ハスケル子「嬉しいというか」
ハスケル子「参照透過性が担保されてるのはいいと思います」

ワイ「たんぽ・・・?」
ワイ「ああ、ワイも20代の頃はずっと仕事でお刺身の上に乗せとったわ」
ハスケル子「それは多分タンポポですね」

社長「(あれはタンポポちゃう、食用菊やで・・・)」

参照透過性 is 何

ワイ「その参照透過性って言葉、たまに見かけるけどどういう意味なん?」

ハスケル子「参照について透過的性質です」

ワイ「うん、せやな
ワイ「それが分からへんいう話なんやけど」

ハスケル子「じゃあまず、参照っていう言葉は」

変数x3という値を参照している

ハスケル子「みたいに使うじゃないですか」

ワイ「せやな」
ワイ「参照は何となく分かるわ」

ハスケル子「じゃあ次は透過的って言葉ですけど」
ハスケル子「透過しちゃうわけなので」
ハスケル子「まるで目に見えない
ハスケル子「つまり存在を意識しないって感じです」

ワイ「ふむふむ」

ハスケル子「だから」
ハスケル子「参照のことを意識せずに扱える
ハスケル子「ってことです」

ワイ「なるほどな・・・」
ワイ「ほんで、それは」
ワイ「どういう意味かいな?

ハスケル子「・・・チッ

ワイ「(舌打ち!?)」

ハスケル子「ええと、コードで説明したほうが早いですね」
ハスケル子「例えば」

keruko.js
let x = 3;

ハスケル子「っていうように」
ハスケル子「xという変数に3を代入したとして」
ハスケル子「それを100行後に」

keruko.js
func(x);

ハスケル子「みたいに使うってなったら」
ハスケル子「どんなことを意識しますか?」

ワイ「ええと」

ワイ「さっきはx3やったけど」
ワイ「100行ほど色々あったから」
ワイ「xは今どんな値を参照してんのかいな?」

ワイ「って思うな」
ワイ「その間の処理を追っかけんとな」

ハスケル子「そうですよね」
ハスケル子「でも純粋関数型言語では」
ハスケル子「再代入という概念がないので」
ハスケル子「一度定義した値は不変です」

ワイ「ほええ」

ハスケル子「つまり」
ハスケル子「一度定義したら、常にx === 3です」

ワイ「なるほどな」
ワイ「再代入が可能な言語の場合は」

ワイ「この変数xは、コンピュータ上の何処かに保存されとる3という値を今は参照しとる・・・」
ワイ「しかし次に使うときに何を参照しとるかは分からへん・・・」

ワイ「ってなるけど」
ワイ「そうやないっちゅうことやな」

ハスケル子「はい」
ハスケル子「xはどこかにある値を参照している、と考えなくても」
ハスケル子「単に3がそこにあるかのように扱えるんです」

ワイ「ふむふむ」

ハスケル子「なので」
ハスケル子「参照のことを意識せずに扱えるってことですね」

ワイ「ほええ」

ハスケル子「変数の値が不変だと、色々と心配事が減るんです」

ワイ「なんとなく分かるで」

ワイ「何かの関数の副作用で」
ワイ「知らんうちxの値が変わっとった!」

ワイ「っちゅうことがないってことやな?」

ハスケル子「そうです」
ハスケル子「そして、変数の値が不変であることによって」
ハスケル子「関数参照透過性が担保されます」

ワイ「関数参照透過性・・・」

ハスケル子「つまり」
ハスケル子「同じ引数を渡せば、常に同じ値を返してくれるんです」

ワイ「なるほどな」
ワイ「関数外の変数とか場面とかによって戻り値が変わったりせぇへん、ということか」
ワイ「それは扱いやすいかもな」

ワイ「でも・・・」
ワイ「逆に場面によって処理を変えたい場合はどうすんの?」
ワイ「例えば・・・」

yametaro.js
let scene = "";

function aisatsu () {
  switch (scene) {
    case "": return "おはよう";
    case "": return "こんばんは";
  }
}

alert(aisatsu());

ワイ「みたいに、」
ワイ「場面によって挨拶を出し分けたいときとか」

ハスケル子「その場合は」

keruko.js
function aisatsu (scene) {
  switch (scene) {
    case "": return "おはよう";
    case "": return "こんばんは";
  }
}

alert(aisatsu(""));

ハスケル子「こんな感じにしますかね」

ワイ「おお」
ワイ「つまり」

ワイ「さあ、あいさつをせえ!」
ワイ「(今はやから、おはようで頼むで?)」

ワイ「て感じやなくて、」

ワイ「朝のあいさつをせえ!」

ワイ「って明示的に指示する感じやな」

ハスケル子「そんな感じです」
ハスケル子「もしこの関数をテストするときとかも」
ハスケル子「朝と夜に2回テストしないでも」
ハスケル子「という2つの引数を与えてテストしてみればいいだけなので」
ハスケル子「テストがしやすいです」

ワイ「なるほどな」

まとめ

  • 純粋関数型言語には
    • 再代入という概念がない
    • 変数の値は不変
    • 関数は、引数が同じであれば常に同じ値を返す(参照透過性

ワイ「↑ってことやね!」

ハスケル子「はい」

でもちょっと気になるワイ

ワイ「でもさ」
ワイ「さっきの変数に、引数としてを与えたらどうなんの?」

ハスケル子「私なら型を自作して以外はブロックします」
ハスケル子「素のJSでは出来ませんけど」

ワイ「なるほどな(型を自作?)」
ワイ「でもその・・・」
ワイ「今はやからっていう引数を与える、っていうのはどうやって判断すんの・・・?」
ワイ「結局はどこかに現在の場面状態みたいなもんを保存しとかなあかんのとちゃうん・・・?」
ワイ「それともワイ、勉強しすぎて頭がおかしくなってしまったん・・・?」

ハスケル子「この先は、実際にやめ太郎さん自身がコードを書いてみたほうが分かりやすいと思います」
ハスケル子「Haskellと似た文法でコードを書いてHTMLやJavaScriptに変換できる、」
ハスケル子「Elmっていう純粋関数型言語があるので」
ハスケル子「それを勉強してみたら色々と実感を持って理解できると思いますよ」

ワイ「Elmちゃんか」
ワイ「聞いたことあるわ」
ワイ「さっそく明日にでもやってみるわ」
ワイ「おおきにやで」

〜「ワイのElmデビュー【前編】」に続く〜