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

Lan*_*nbo 30 haskell strictness

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

data Foo = Bar !Int !Float
Run Code Online (Sandbox Code Playgroud)

我经常认为懒惰是一件好事.现在,当我浏览消息来源时,我会看到比非!变体更严格的字段.

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

ehi*_*ird 36

除非您在Int和Float字段中存储大量计算,否则可能会产生大量开销,这些开销是通过在thunk中构建的大量繁琐计算构建的.例如,如果您在数据类型中对一个惰性Float字段重复添加1,它将耗尽越来越多的内存,直到您实际强制该字段计算它.

通常,您希望在字段中存储昂贵的计算.但是如果你知道你不会提前做任何类似的事情,你可以严格标记该字段,并避免必须手动添加seq到任何地方以获得所需的效率.

作为额外的奖励,当给定标志-funbox-strict-fieldsGHC将数据类型的严格字段1直接解包到数据类型本身时,这是可能的,因为它知道它们将始终被评估,因此不必分配thunk; 在这种情况下,Bar值将包含直接位于内存中Bar值内的Int和Float的机器字,而不是包含指向包含数据的thunk的两个指针.

懒惰是一个非常有用的东西,但有些时候,它只是妨碍了计算,特别是对于总是看(并因此强制)的小字段,或经常修改但从不用非常昂贵的计算.严格的字段有助于克服这些问题,而无需修改数据类型的所有用途.

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

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

data Exp = Infix Op Exp Exp
         | ...

data Op = Add | Subtract | Multiply | Divide
Run Code Online (Sandbox Code Playgroud)

您不希望使Exp字段严格,因为应用这样的策略意味着每当您查看顶级节点时都会评估整个AST,这显然不是您想要从懒惰中获益的.但是,该Op字段永远不会包含您希望延迟到日期的昂贵计算,并且如果您有真正深度嵌套的解析树,则每个中缀运算符的thunk开销可能会变得昂贵.所以对于中缀构造函数,你想要使Op字段严格,但是让两个Exp字段保持惰性.

1只能解压缩单构造函数类型.


muc*_*aho 8

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

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

看看参数的评估深度很有意思- 就像对WHNF一样评估seq$!评估.

给定数据类型

data Foo = IntFoo !Int | FooFoo !Foo | BarFoo !Bar
data Bar = IntBar Int
Run Code Online (Sandbox Code Playgroud)

表达方式

let x' = IntFoo $ 1 + 2 + 3
in  x'
Run Code Online (Sandbox Code Playgroud)

评估为WHNF产生值IntFoo 6(==完全评估,== NF).
另外这个表达

let x' = FooFoo $ IntFoo $ 1 + 2 + 3
in  x'
Run Code Online (Sandbox Code Playgroud)

评估为WHNF产生值FooFoo (IntFoo 6)(==完全评估,== NF).
但是,这个表达方式

let x' = BarFoo $ IntBar $ 1 + 2 + 3
in  x'
Run Code Online (Sandbox Code Playgroud)

评估为WHNF产生值BarFoo (IntBar (1 + 2 + 3))(!=完全评估,!= NF).

要点:!Bar如果数据构造Bar函数本身不包含严格的参数,则参数的严格性不一定有用.

  • @wenlong 查看另一个 [关于可视化 thunk 的答案](http://stackoverflow.com/questions/16767028/any-way-to-visualize-a-thunk-function-or-how-to-view-a-function- for-a-general),特别是工具 [`ghc-vis`](http://felsin9.de/nnis/ghc-vis/#basic-usage) (2认同)