あなたは一人じゃない。 seq
おそらく、いくつかの異なる理由から、適切に使用するのが最も難しい Haskell 関数の 1 つです。あなたの最初の例では:
foo s t = seq q (bar q t) where
q = s*t
q
が評価される前bar q t
に評価されます。が評価されない場合bar q t
は、どちらも評価されq
ません。だからあなたが持っているなら
main = do
let val = foo 10 20
return ()
使用さval
れないため、評価されません。なのでq
評価もされません。代わりに持っている場合
main = print (foo 10 20)
の結果はfoo 10 20
(によってprint
) 評価されるため、 withinfoo
q
は の結果の前に評価されbar
ます。
これが機能しない理由でもあります。
myseq x = seq x x
意味的には、これは2 番目が評価x
される前に 1 番目x
が評価されることを意味します。しかし、2 番目x
が評価されない場合、1 番目も評価される必要はありません。Soseq x x
は とまったく同じx
です。
2 番目の例は、同じものである場合とそうでない場合があります。ここで、式s*t
は の出力の前に評価されますが、 への最初のパラメーターbar
と同じではない場合があります。コンパイラが共通部分式の削除を実行すると、2 つの同一の式が共通化される場合があります。ただし、GHC は CSE を実行する場所についてかなり保守的である可能性があるため、これに頼ることはできません。私が定義すると、バーでその値を使用する前にCSE を実行して評価します。より複雑な式ではそうではない場合があります。s*t
bar
bar q t = q*t
s*t
また、厳密な評価の意味を知りたい場合もあります。 seq
最初の引数を弱いヘッド正規形 (WHNF) に評価します。これは、データ型の場合、最も外側のコンストラクターをアンパックすることを意味します。このことを考慮:
baz xs y = seq xs (map (*y) xs)
xs
のため、リストでなければなりませんmap
。がそれseq
を評価すると、本質的にコードが次のように変換されます。
case xs of
[] -> map (*y) xs
(_:_) -> map (*y) xs
これは、リストが空かどうかを判断し、2 番目の引数を返すことを意味します。リスト値は評価されないことに注意してください。だからあなたはこれを行うことができます:
Prelude> seq [undefined] 4
4
しかし、これではありません
Prelude> seq undefined 5
*** Exception: Prelude.undefined
s の最初の引数に使用するデータ型が何であれseq
、WHNF への評価は、コンストラクターを理解するのに十分であり、それ以上ではありません。データ型に、強打パターンで厳密とマークされているコンポーネントが含まれている場合を除きます。次に、すべての厳格なフィールドも WHNF に評価されます。
編集:(コメントで提案してくれたDaniel Wagnerに感謝します)
関数の場合seq
、関数が「ラムダが表示される」まで式を評価します。これは、関数を適用する準備ができていることを意味します。これが何を意味するかを示すいくつかの例を次に示します。
-- ok, lambda is outermost
Prelude> seq (\x -> undefined) 'a'
'a'
-- not ok. Because of the inner seq, `undefined` must be evaluated before
-- the lambda is showing
Prelude> seq (seq undefined (\x -> x)) 'b'
*** Exception: Prelude.undefined
ラムダ バインディングを (組み込みの) データ コンストラクターと考えると、seq
on 関数はデータでの使用と完全に一致します。
また、「ラムダ バインディング」は、ラムダ表記で定義されているか、通常の関数として定義されているかに関係なく、すべてのタイプの関数定義を包含します。
HaskellWiki の seq ページの論争セクションには、関数に関連する in の結果のいくつかについて少し書かseq
れています。