严格声明有什么意义?

o1i*_*ver 11 haskell lazy-evaluation

我正在启动Haskell并且正在查看一些使用"!"定义数据类型的库.bytestring库中的示例:

data ByteString = PS {-# UNPACK #-} !(ForeignPtr Word8) -- payload
                     {-# UNPACK #-} !Int                -- offset
                     {-# UNPACK #-} !Int                -- length
Run Code Online (Sandbox Code Playgroud)

现在我看到这个问题是对这意味着什么的解释,我想这很容易理解.但我现在的问题是:使用它有什么意义?由于表达式将在需要时进行评估,为什么要强制进行早期评估?

在这个问题的第二个答案中,CV汉森说:"[...]有时懒惰的开销可能太多或浪费".这是否意味着它用于节省内存(保存值比保存表达式便宜)?

一个解释和一个例子将是伟大的!

谢谢!

[ 编辑 ]我想我应该选择一个没有{ - #UNPACK# - }的例子.所以让我自己做一个.这会有意义吗?是的,为什么以及在什么情况下?

data MyType = Const1 !Int
            | Const2 !Double
            | Const3 !SomeOtherDataTypeMaybeMoreComplex
Run Code Online (Sandbox Code Playgroud)

bdo*_*lan 13

这里的目标不是严格,而是将这些元素打包到数据结构中.如果没有严格性,那么这三个构造函数参数中的任何一个都可以指向堆分配的值结构或堆分配的延迟评估thunk.严格来说,它只能指向堆分配的值结构.严格和紧凑的结构,可以使这些值内联.

由于这三个值中的每一个都是一个指针大小的实体,并且无论如何都是严格访问的,因此强制使用严格的打包结构可以在使用此结构时保存指针间接.

在更一般的情况下,严格性注释可以帮助减少空间泄漏.考虑这样的情况:

data Foo = Foo Int

makeFoo :: ReallyBigDataStructure -> Foo
makeFoo x = Foo (computeSomething x)
Run Code Online (Sandbox Code Playgroud)

没有严格的注释,如果你只是调用makeFoo,它将构建一个Foo指向thunk的指向ReallyBigDataStructure,将其保留在内存中直到某些东西迫使thunk进行评估.如果我们改为

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

这迫使computeSomething评估立即进行(好吧,一旦某些东西强迫makeFoo本身),这就避免了引用它ReallyBigDataStructure.

请注意,这是与bytestring代码不同的用例; bytestring代码非常频繁地强制其参数,因此不太可能导致空间泄漏.最好将bytestring代码解释为纯优化以避免指针解引用.

  • 还有很多情况下,额外的严格性是安全的,编译器*可以*证明它,并且确实会优化您的代码.GHC非常聪明,即使不是无所不知(至少在当前版本中). (2认同)