編集:実際の質問のブルズアイを見逃したようですが、私の答えはかなり良いと思うので、そのままにしておきます:-)(以下を参照)。
質問をより簡潔に言い表すと、次のようになると思います。
まず、C のような命令型言語を使用して、変数を定義した後に変数を変更できないように作成したとします。例えば:
int i;
for (i = 0; // okay, that's one assignment
i < 10; // just looking, that's all
i++) // BUZZZ! Sorry, can't do that!
さて、あなたのfor
ループがあります。while
ループを維持できますか?
while (i < 10)
確かに、しかしそれはあまり役に立ちません。 i
変更できないため、永久に実行されるか、まったく実行されないかのいずれかです。
再帰はどうですか?はい、再帰を維持できますが、それでも十分に便利です。
int sum(int *items, unsigned int count)
{
if (count) {
// count the first item and sum the rest
return *items + sum(items + 1, count - 1);
} else {
// no items
return 0;
}
}
さて、関数では状態を変更しませんが、変数は変化する可能性があります。変数が関数に渡されると、ロックされます。ただし、関数を再度呼び出すことができます (再帰)。これは、まったく新しい変数のセットを取得するようなものです (古い変数は同じままです)。との複数のインスタンスがありますがitems
、count
はsum((int[]){1,2,3}, 3)
常に に評価される6
ため、必要に応じてその式を に置き換えることができ6
ます。
私たちはまだやりたいことを何でもできますか?100%確実ではありませんが、答えは「イエス」だと思います。ただし、クロージャーがある場合は確かにできます。
あなたはそれを正しく持っています。アイデアは、変数が定義されると、再定義できないということです。同じ変数を指定すると、参照透過式は常に同じ結果値を生成します。
純粋に関数型の言語である Haskell を検討することをお勧めします。厳密に言えば、Haskell には「代入」演算子がありません。例えば:
my_sum numbers = ??? where
i = 0
total = 0
ここでは、i と total をインクリメントする "for ループ" を書くことはできません。ただし、すべてが失われるわけではありません。再帰を使用して、新しいi
とを取得し続けるだけtotal
です。
my_sum numbers = f 0 0 where
f i total =
if i < length numbers
then f i' total'
else total
where
i' = i+1
total' = total + (numbers !! i)
(これは Haskell でリストを合計するばかげた方法ですが、単一の割り当てに対処する方法を示していることに注意してください。)
次に、この非常に命令的なコードを考えてみましょう。
main = do
a <- readLn
b <- readLn
print (a + b)
実際には、次の構文糖衣です。
main =
readLn >>= (\a ->
readLn >>= (\b ->
print (a + b)))
main がステートメントのリストで構成される関数である代わりに、main は Haskell が実行する IO アクションであり、アクションが定義され、バインド操作と一緒に連鎖されるという考え方です。また、関数を使用して、何もせずに任意の値を生成するアクションを定義できますreturn
。
bind と return はアクションに固有のものではないことに注意してください。それらは、あらゆる種類のファンキーなことを行うために、自分自身を Monad と呼ぶ任意の型で使用できます。
明確にするために、 を検討してreadLn
ください。 readLn
実行されると、標準入力から行を読み取り、その解析値を生成するアクションです。その値で何かをするために、変数に格納することはできません。参照透過性に違反するからです。
a = readLn
これが許可されている場合、 a の値は世界に依存し、 を呼び出すたびに異なります。readLn
つまり、readLn
参照透過性がなくなります。
代わりに、アクションを処理する関数に readLn アクションをバインドし、次のように新しいアクションを生成します。
readLn >>= (\x -> print (x + 1))
この式の結果はアクション値です。Haskell がソファから降りてこのアクションを実行すると、整数を読み取り、インクリメントして出力します。アクションの結果を、その結果で何かを行う関数にバインドすることにより、状態の世界で遊んでいる間、参照の透過性を維持することができます。