このFAQには、
seq演算子は
seq :: a -> b -> b
x
seq
yはxを評価し、それがボトムでないことを確認してから、結果を破棄してyを評価します。これは役に立たないように思われるかもしれませんが、yが考慮される前にxが評価されることが保証されていることを意味します。
それはHaskellにとってはひどく素晴らしいことですが、それは
x `seq` f x
評価の費用はx
2回支払われます(「結果を破棄する」)?
このFAQには、
seq演算子は
seq :: a -> b -> b
x
seq
yはxを評価し、それがボトムでないことを確認してから、結果を破棄してyを評価します。これは役に立たないように思われるかもしれませんが、yが考慮される前にxが評価されることが保証されていることを意味します。
それはHaskellにとってはひどく素晴らしいことですが、それは
x `seq` f x
評価の費用はx
2回支払われます(「結果を破棄する」)?
関数はのseq
値を破棄しますがx
、値が評価されているため、へのすべての参照x
は、未評価のバージョンを指すのではなく、評価されたバージョンを指すように「更新」されx
ます。したがって、をseq
評価して破棄x
しても、他のユーザーについても値が評価されているため、x
評価が繰り返されることはありません。
いいえ、それは計算ではなく、忘れてしまいます。それは計算です-これはキャッシュを強制します。
たとえば、次のコードについて考えてみます。
let x = 1 + 1
in x + 1
Haskellは怠惰なので、これはに評価され((1 + 1) + 1)
ます。サンクと1の合計を含むサンク。内側のサンクは1プラス1です。
怠惰でない言語であるjavascriptを使用して、これがどのように見えるかを示しましょう。
function(){
var x = function(){ return 1 + 1 };
return x() + 1;
}
このようにサンクをつなぎ合わせると、スタックオーバーフローが発生する可能性があるため、繰り返し実行seq
すると救済されます。
let x = 1 + 1
in x `seq` (x + 1)
これが評価されると言ったとき、私は嘘をついています(2 + 1)
が、それはほぼ真実です-残りが発生する前に2の計算が強制的に行われるというだけです(ただし、2はまだ怠惰に計算されます)。
javascriptに戻ります:
function(){
var x = function(){ return 1 + 1 };
return (function(x){
return x + 1;
})( x() );
}
私はx
一度だけ評価されると信じています(そして、怠惰な操作で一般的であるように、結果は将来の使用のために保持されます)。その振る舞いがseq
役に立ちます。
もちろんseq
、それ自体は何も「評価」しません。強制順序の依存関係を記録するだけです。強制自体は、パターンマッチングによってトリガーされます。seq x (f x)
強制されると、最初にx
強制され(結果の値をメモ化)、次にf x
強制されます。Haskellの遅延評価は、式の強制の結果を記憶することを意味するため、繰り返しの「評価」(ここでは恐ろしい引用)は実行されません。
「評価」は完全な評価を意味するので、怖い引用符で囲みます。Haskellウィキブックスの言葉で、
「Haskellの値は高度に階層化されています。Haskellの値を「評価する」とは、これらの層のいずれかまで評価することを意味する可能性があります。」
繰り返しになりますseq
が、それ自体は何も評価しません。 いかなる状況でもseq x x
評価されません。レポートが言っているように見えることとは反対に、は何も評価しません。x
seq x (f x)
f = id
いつでも確認できますunsafePerformIO
またはtrace
…</p>
import System.IO.Unsafe (unsafePerformIO)
main = print (x `seq` f (x + x))
where
f = (+4)
x = unsafePerformIO $ print "Batman!" >> return 3