これは少しあいまいかもしれませんが、私はしばらくの間それを疑問に思っていました。私の知る限り!
、値が作成される前に、データコンストラクターのパラメーターが評価されていることを確認できます。
data Foo = Bar !Int !Float
怠惰は素晴らしいことだとよく思います。!
さて、ソースを調べると、 -lessバリアントよりも厳密なフィールドが頻繁に表示されます。
これの利点は何ですか、そしてなぜ私はそれをそのまま怠惰にすべきではないのですか?
これは少しあいまいかもしれませんが、私はしばらくの間それを疑問に思っていました。私の知る限り!
、値が作成される前に、データコンストラクターのパラメーターが評価されていることを確認できます。
data Foo = Bar !Int !Float
怠惰は素晴らしいことだとよく思います。!
さて、ソースを調べると、 -lessバリアントよりも厳密なフィールドが頻繁に表示されます。
これの利点は何ですか、そしてなぜ私はそれをそのまま怠惰にすべきではないのですか?
大規模な計算を Int および Float フィールドに格納していない限り、サンクに蓄積された多くの些細な計算から、かなりのオーバーヘッドが蓄積される可能性があります。たとえば、データ型の遅延 Float フィールドに 1 を繰り返し追加すると、実際にフィールドを強制して計算するまで、ますます多くのメモリが消費されます。
多くの場合、コストのかかる計算をフィールドに格納する必要があります。しかし、事前にそのようなことをしないことがわかっている場合は、フィールドを厳密にマークして、必要seq
な効率を得るために手動で追加する必要をなくすことができます。
追加のボーナスとして、フラグを指定すると、-funbox-strict-fields
GHC はデータ型の厳密なフィールド1をデータ型自体に直接アンパックします。これは、それらが常に評価されることを知っているため可能であり、したがってサンクを割り当てる必要はありません。この場合、Bar 値は、データを含むサンクへの 2 つのポインタを含むのではなく、メモリ内の Bar 値の内部に直接 Int と Float を構成するマシン ワードを含みます。
怠惰は非常に便利なことですが、特に常に見られる (したがって強制される) 小さなフィールドや、頻繁に変更されるが非常に高価な計算では決して行われない小さなフィールドの場合は、単に邪魔になり、計算を妨げることがあります。厳密なフィールドは、データ型のすべての使用を変更することなく、これらの問題を解決するのに役立ちます。
遅延フィールドよりも一般的かどうかは、読んでいるコードの種類によって異なります。たとえば、関数ツリー構造が厳密なフィールドを広範囲に使用することはほとんどありません。
中置操作用のコンストラクターを持つ AST があるとします。
data Exp = Infix Op Exp Exp
| ...
data Op = Add | Subtract | Multiply | Divide
Exp
そのようなポリシーを適用すると、最上位ノードを見るたびに AST 全体が評価されることになるため、フィールドを厳密にすることは望ましくありません。ただし、Op
後日に延期したい高価な計算がフィールドに含まれることは決してなく、本当に深くネストされた解析ツリーがある場合、中置演算子ごとのサンクのオーバーヘッドが高くなる可能性があります。したがって、中置コンストラクターの場合、Op
フィールドを正格にし、2 つのExp
フィールドを遅延させておきます。
1単一コンストラクター型のみをアンパックできます。
他の回答で提供される情報に加えて、次の点に注意してください。
私の知る限り
!
、値が構築される前にデータコンストラクターのパラメーターが評価されていることを確認できます
パラメーターが評価される深さを見るのは興味深いことです。これはWHNFと同様にseq
評価$!
されます。
与えられたデータ型
data Foo = IntFoo !Int | FooFoo !Foo | BarFoo !Bar
data Bar = IntBar Int
表現
let x' = IntFoo $ 1 + 2 + 3
in x'
WHNF に評価されると、値が生成されますIntFoo 6
(== 完全に評価される、== NF)。
さらにこんな表現
let x' = FooFoo $ IntFoo $ 1 + 2 + 3
in x'
WHNF に評価されると、値が生成されますFooFoo (IntFoo 6)
(== 完全に評価される、== NF)。
しかし、この表現は
let x' = BarFoo $ IntBar $ 1 + 2 + 3
in x'
WHNF に評価されると値が生成されますBarFoo (IntBar (1 + 2 + 3))
(!= 完全に評価され、!= NF)。
要点:のデータ コンストラクターに厳密なパラメーター自体が含まれていない場合、パラメーターの厳密性が!Bar
必ずしも役立つとは限りません。Bar
怠惰に関連するオーバーヘッドがあります。コンパイラは、結果が必要になるまで計算を格納するために、値のサンクを作成する必要があります。遅かれ早かれ常に結果が必要になることがわかっている場合は、結果の評価を強制することは理にかなっています。
怠惰には代償が伴います。
コストは 2 倍です。