web-dev-qa-db-ja.com

なぜシーケンスが悪いのですか?

Haskellには、seqという名前の魔法の関数があり、任意の型の引数を取り、それをWeak Head Normal Form(WHNF)に減らします。

「多態性seqは悪い」と主張するいくつかの情報源を読みました[それらが誰であったか思い出せない ...]。彼らはどのように「悪い」のですか?

同様に、引数をNormal Form(NF)に減らすrnf関数があります。しかしthisはクラスメソッドです。任意の型に対しては機能しません。 seqと同様に、言語仕様を変更して、組み込みプリミティブとしてこれを提供できるように思えます。これは、おそらく、seqを使用するよりも「さらに悪い」と思われます。これはどのようにですか?

最後に、誰かが、seqrnfparなどをid関数ではなく、const関数と同じ型にすることで改善されることを示唆しました。どうして?

57

多態性seq関数は、自由定理を弱めるため、つまり、seqなしでは有効であるいくつかの等式は、seqでは無効になります。 。たとえば、平等

map g (f xs) = f (map g xs)

すべての関数g :: tau -> tau'、すべてのリストxs :: [tau]、およびすべての多態性関数f :: [a] -> [a]を保持します。基本的に、この同等性は、fが引数リストの要素を並べ替えたり、要素を削除または複製したりするだけで、新しい要素を生み出すことはできないことを示しています。

正直に言うと、エラーのタイプは多態性であるため、終了しない計算/実行時エラーをリストに「挿入」する可能性があるため、要素を発明できます。つまり、この平等はseqなしのHaskellのようなプログラミング言語ではすでに破られています。次の関数定義は、方程式の反例を示しています。基本的に、左側でgはエラーを「非表示」にします。

g _ = True
f _ = [undefined]

方程式を修正するには、gを厳密にする必要があります。つまり、エラーをエラーにマップする必要があります。この場合、平等が再び成立します。

たとえば、多形のseq演算子を追加すると、方程式は再び壊れます。たとえば、次のインスタンス化は反例です。

g True = True
f (x:y:_) = [seq x y]

リストxs = [False, True]を検討すると、

map g (f [False, True]) = map g [True] = [True]

しかし、一方では

f (map g [False, True]) = f [undefined, True] = [undefined]

つまり、seqを使用して、リストの特定の位置の要素を、リスト内の別の要素の定義に依存させることができます。 gが合計である場合、同等性は再び成立します。無料の定理に興味がある場合は、 無料の定理ジェネレータ を確認してください。これにより、エラーのある言語、またはseqの言語を検討するかどうかを指定できます。これはあまり実用的ではないように思えるかもしれませんが、seqは、関数プログラムのパフォーマンスを向上させるために使用されるいくつかの変換を中断します。たとえば、foldr/build融合は失敗しますseqが存在する場合。 seqが存在する場合の自由定理の詳細に興味がある場合は、- seqの存在における自由定理 を調べてください。

私が知る限り、多相seqが言語に追加されたときに、特定の変換が壊れることがわかっていました。ただし、代替案にも欠点があります。タイプクラスベースのseqを追加する場合、seqをどこかに追加すると、多くのタイプクラス制約をプログラムに追加しなければならない場合があります。さらに、seqを使用して修正できるスペースリークがあることがすでにわかっていたため、seqを省略することはできませんでした。

最後に、何かを見落とすかもしれませんが、a -> a型のseq演算子がどのように機能するかわかりません。 seqの手がかりは、別の式が正規形の先頭にあると評価された場合、式を評価して正規形の先頭にあることです。 seqのタイプがa -> aの場合、1つの式の評価を別の式の評価に依存させる方法はありません。

52

別の反例を この答え に示します-モナドはsequndefinedでモナドの法則を満たしません。そして、チューリング完全言語ではundefinedは避けられないので、非難すべきはseqです。

8
Petr Pudlák