Haskellの特徴である参照透過性とは?

Haskellの参照透過性という特徴について試していこうと思います。
読んでいる本はこちらです。
916Tv9qiy8L
すごいHaskellたのしく学ぼう!

参照透過性とは?

Wikipediaによるとこうです。

ある式が参照透過であるとは、その式をその式の値に置き換えてもプログラムの振る舞いが変わらないことを言う。

参照透過性 – Wikipeda
「式」を「式の値」に置き換えても変わらない、ということだそうです。
Haskellには遅延評価という特徴がありました。
無限の長さの配列を扱うこともできる、Haskellの遅延評価とは
遅延評価を実現するためには、「今、評価した結果」と「後で評価した結果」が変わっていてはいけません。「遅延で評価したら、結果が変わっていた」ではいけません・・・。
この「いつ評価しても結果が変わらない」という特徴が参照透過性です。

PHPで書いてみる

PHPでたとえば、こういう処理を書いてみます。

$multiply = 2;
$answer1 = 10 * $multiply;
print $answer1;
// => 20

参照透過性とは、「式」を「式の値」に変えても、結果が変わらない、ということでした。
ということは、式「10 * $multiply」を、式の値「20」に変えても良い、ということです。

$multiply = 2;
$answer1 = 20;
print $answer1;
// => 20

参照透過っぽいですね。
では、この場合はどうでしょうか。途中で、$multiplyに、3を再代入しています。

$multiply = 2;
$answer1 = 10 * $multiply;
$multiply = 3;
$answer2 = $answer1 + 10 * $multiply;
print $answer2;
// => 50

「10 * $multiply」を「20」に置き換えてしまうと、結果が変わってしまいます。

$multiply = 2;
$answer1 = 20;
$multiply = 3;
$answer2 = $answer1 + 20;
print $answer2;
// => 40

この場合は、「10 * $multiply」という式は、「参照透過ではない」ということになります。

Haskellで同じように参照透過を破ろうとしてみる

Haskellで同じような処理を書いてみます。

multiply = 2
answer1 = 10 * multiply;
main = putStrLn $ show answer1
// => 20

これは想定どおりです。
では、multiplyに3を再代入して、参照透過を破ってみようと思います。

multiply = 2
answer1 = 10 * multiply
multiply = 3
answer2 = answer1 + 10 * multiply
main = putStrLn $ show answer2

この場合は、コンパイルが通りません。

main.hs:3:1: error:
    Multiple declarations of ‘multiply’
    Declared at: main.hs:1:1
                 main.hs:3:1
  |
3 | multiply = 3
  | ^^^^^^^^

参照透過性を破ることはできませんでした。
Haskellでは、再代入ができません。これによって、一度定義された値は、変わることがありません。
参照透過性が保たれていて、いつ評価しても同じなので、遅延評価が可能になります。

まだ定義されていない値を使う

いつ評価しても変わらないということは、いつ定義してもいい、とも言えるかもしれません。

answer1 = 10 * multiply
multiply = 2
main = putStrLn $ show answer1
// => 20

ソースコードの1行目と2行目を入れ替えてみました。
少し奇妙に見えますが、これをコンパイルしてもエラーは出ず、問題なく実行されます。
手続き型言語のように、上から順に処理していっている、というわけではないが、よく分かります。
Haskellって面白いですねえ!

タイトルとURLをコピーしました