Haskellには、seq
という名前の魔法の関数があり、任意の型の引数を取り、それをWeak Head Normal Form(WHNF)に減らします。
「多態性seq
は悪い」と主張するいくつかの情報源を読みました[それらが誰であったか思い出せない今 ...]。彼らはどのように「悪い」のですか?
同様に、引数をNormal Form(NF)に減らすrnf
関数があります。しかしthisはクラスメソッドです。任意の型に対しては機能しません。 seq
と同様に、言語仕様を変更して、組み込みプリミティブとしてこれを提供できるように思えます。これは、おそらく、seq
を使用するよりも「さらに悪い」と思われます。これはどのようにですか?
最後に、誰かが、seq
、rnf
、par
などをid
関数ではなく、const
関数と同じ型にすることで改善されることを示唆しました。どうして?
多態性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つの式の評価を別の式の評価に依存させる方法はありません。
別の反例を この答え に示します-モナドはseq
とundefined
でモナドの法則を満たしません。そして、チューリング完全言語ではundefined
は避けられないので、非難すべきはseq
です。