2011-06-03 114 views
11

我开始Haskell,正在看一些数据类型用“!”定义的库。从字节串库的例子:严格声明的要点是什么?

data ByteString = PS {-# UNPACK #-} !(ForeignPtr Word8) -- payload 
        {-# UNPACK #-} !Int    -- offset 
        {-# UNPACK #-} !Int    -- length 

现在我看到this question作为这意味着什么解释,我想这是很容易理解的。但我现在的问题是:使用这个的意义何在?既然表达式会在需要时进行评估,为什么要强制早期评估呢?

在对这个问题的第二个答案C.V.汉森说:“有时懒惰的开销可能太多或浪费了”。这是否意味着它用于节省内存(保存该值比保存表达式更便宜)?

一个解释和一个例子会很棒!

谢谢!

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

data MyType = Const1 !Int 
      | Const2 !Double 
      | Const3 !SomeOtherDataTypeMaybeMoreComplex 
+0

懒惰可以对代码产生很大的影响,主要的原因是当严格程序需要恒定的空间时,懒惰程序通常在线性空间中运行。但在你的例子中,真正的原因可能与外部函数操纵的数据有关,这些外部函数不知道如何强制懒惰评估。 – 2011-06-03 19:45:24

+0

没有。ForeignPtr可以传递给外部函数,当然,但不是结构本身 - 即使是严格/解压缩注释,没有为本地haskell结构定义ABI – bdonlan 2011-06-03 19:51:00

回答

13

这里的目标并不是严格的把这些元素包装到数据结构中。没有严格的规定,这三个构造函数参数中的任何一个都可以指向堆分配值结构或堆分配延迟评估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参考。

请注意,这是与字节串代码不同的用例;字节串代码相当频繁地强制它的参数,所以它不太可能导致空间泄漏。最好将字节串代码解释为纯粹的优化,以避免指针取消引用。

+0

也许我应该选择一个没有打包/解包的例子(我不明白/还没有看过)。无论如何,“指针大小”实体的含义是什么?我想我对这些指针有点困惑,因为根据我的理解,Haskell并没有真正的指针。这是一些较低级别的编译器相关的东西(这不是haskell本身的一部分)? – o1iver 2011-06-03 20:09:55

+0

是的,这是结构的实际内存表示的低级优化。 – bdonlan 2011-06-03 20:10:38

+0

好吧,这是有道理的。不过,我编辑了我的答案,并添加了另一个例子(^^)。在这种情况下,相同的答案是否有效? – o1iver 2011-06-03 20:20:48