Haskell 严格声明的意义是什么?
我开始学习Haskell,并研究了一些数据类型定义为“!”的库。bytestring库中的示例:Haskell 严格声明的意义是什么?,haskell,lazy-evaluation,Haskell,Lazy Evaluation,我开始学习Haskell,并研究了一些数据类型定义为“!”的库。bytestring库中的示例: data ByteString = PS {-# UNPACK #-} !(ForeignPtr Word8) -- payload {-# UNPACK #-} !Int -- offset {-# UNPACK #-} !Int -- length 现
data ByteString = PS {-# UNPACK #-} !(ForeignPtr Word8) -- payload
{-# UNPACK #-} !Int -- offset
{-# UNPACK #-} !Int -- length
现在我把它看作是对这意味着什么的一种解释,我想这很容易理解。但我现在的问题是:使用这个有什么意义?既然表达式将在需要时进行求值,为什么要强制进行早期求值
在对这个问题的第二个回答中,C.V.汉森说:“[…]有时懒散的开销可能太多或浪费”。这是否意味着它用于节省内存(保存值比保存表达式便宜)
一个解释和一个例子将是伟大的
谢谢
[EDIT]我想我应该选择一个没有{-#UNPACK}的例子。让我自己做一个。这有意义吗?是的,为什么以及在什么情况下
data MyType = Const1 !Int
| Const2 !Double
| Const3 !SomeOtherDataTypeMaybeMoreComplex
这里的目标不是严格,而是将这些元素打包到数据结构中。如果不严格,这三个构造函数参数中的任何一个都可以指向堆分配的值结构或堆分配的延迟计算thunk。严格来说,它只能指向堆分配的值结构。有了严格性和压缩结构,就可以将这些值内联 由于这三个值中的每一个都是指针大小的实体,并且无论如何都是严格访问的,因此在使用此结构时强制使用严格的压缩结构可以节省指针间接指向 在更一般的情况下,严格性注释有助于减少空间泄漏。考虑这样一个例子:
data Foo = Foo Int
makeFoo :: ReallyBigDataStructure -> Foo
makeFoo x = Foo (computeSomething x)
没有严格性注释,如果您只调用makeFoo
,它将构建一个Foo
,指向指向ReallyBigDataStructure
的thunk,将其保留在内存中,直到有东西迫使thunk求值。如果我们有
data Foo = Foo !Int
这迫使computeSomething
计算立即进行(好吧,只要有东西迫使makeFoo本身),这避免了留下对ReallyBigDataStructure
的引用
注意,这是一个不同于bytestring代码的用例;bytestring代码频繁地强制执行其参数,因此不太可能导致空间泄漏。最好将bytestring代码解释为纯粹的优化,以避免指针解引用。惰性会对代码产生很大影响,主要原因是,当严格的程序需要常量空间时,惰性程序通常在线性空间中运行。但在您的示例中,真正的原因可能与所讨论的数据被不知道如何强制惰性计算的外部函数操纵有关。不。当然,ForeignPtr可以传递给外部函数,但不能传递给结构本身-即使使用严格的/unpack注释,也没有为原生haskell结构定义ABI。可能我应该选择一个没有打包/解包的示例(我不理解/还没有看)。在任何情况下,“指针大小”实体是什么意思?我想我对指针有点困惑,因为据我所知,Haskell并没有真正的指针。这是某种低级编译器相关的东西吗(这不是haskell本身的一部分)?是的,这是对结构的实际内存表示的低级优化。好的,这是有意义的。但是,我编辑了我的答案,并添加了另一个示例(^^)。同样的答案在这种情况下有效吗?太好了!这正是我需要解释的。虽然我必须说,我本以为编译器会优化它。我的意思是,从我目前的理解来看,懒散的关键是表现。。。不过还是要谢谢你的回答!还有相当多的情况下,超严格是安全的,编译器可以证明这一点,并将确实优化您的代码。GHC非常聪明,即使不是无所不知的(至少在当前版本中是如此)。