数据类型中严格字段的​​优点

这现在可能有点模糊,但我一直在想。 以我的知识! ,可以确保在构造值之前正在评估数据构造函数的参数:

data Foo = Bar !Int !Float

我经常认为懒惰是件好事。 现在,当我通过消息来源时,我经常看到严格的领域比! 无变体。

这有什么好处,为什么我不应该保持懒惰?


除非在Int和Float字段中存储大量计算,否则大量的繁琐计算可能会在thunk中累积。 例如,如果您重复将1添加到数据类型的惰性Float字段中,它将占用越来越多的内存,直到您实际强制该字段并计算它为止。

通常情况下,你想在一个领域存储昂贵的计算。 但是如果你知道你以前不会做这种事情,那么你可以严格地标记这个字段,并避免在任何地方手动添加seq来获得你想要的效率。

作为附加的好处,给出的标志时-funbox-strict-fields GHC将解包的数据类型的严格fields1直接进入数据类型本身,这是可能的,因为它知道它们将总是被评估,因此没有形实转换已经被分配; 在这种情况下,Bar值将包含直接在内存中的Bar值内部包含Int和Float的机器字,而不是包含指向包含该数据的thunk的两个指针。

懒惰是一件非常有用的事情,但有些时候它会妨碍计算,特别是对于那些始终被查看(并因此被迫)或者经常被修改,但从来不需要昂贵的计算的小型领域。 严格的字段有助于克服这些问题,而无需修改数据类型的所有用途。

它是否比懒惰的字段更普遍取决于您正在阅读的代码的类型; 例如,你不可能看到任何功能性树结构广泛使用严格的字段,因为它们从懒惰中受益匪浅。

假设您有一个带有构造函数的AST,用于中缀操作:

data Exp = Infix Op Exp Exp
         | ...

data Op = Add | Subtract | Multiply | Divide

您不希望使Exp字段严格,因为应用这样的策略意味着每当您查看顶级节点时都会评估整个AST,这显然不是您想从懒惰中受益的。 然而, Op域永远不会包含昂贵的计算,您希望推迟到以后的日期,并且如果您拥有真正深层嵌套的分析树,则每个中缀运算符的thunk开销可能会变得非常昂贵。 因此,对于中缀构造函数,您希望使Op字段严格,但将两个Exp字段留空。

1只有单构造类型可以解压缩。


除了其他答案提供的信息外,请记住:

以我的知识! ,可以确保在构造值之前正在评估数据构造函数的参数

看看参数评估有多 - 这很有趣,就像seq$! 评估为WHNF。

给定数据类型

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参数的严格性不一定有帮助。


与懒惰相关的开销 - 编译器必须为该值创建一个thunk来存储计算,直到需要结果为止。 如果您知道迟早总会需要结果,那么强制评估结果是有意义的。

链接地址: http://www.djcxy.com/p/42965.html

上一篇: Advantages of strict fields in data types

下一篇: To what extent is Haskell lazy?