Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the point of the strictness declaration?

I am starting Haskell and was looking at some libraries where data types are defined with "!". Example from the bytestring library:

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

Now I saw this question as an explanation of what this means and I guess it is fairly easy to understand. But my question is now: what is the point of using this? Since the expression will be evaluated whenever it is need, why would you force the early evaluation?

In the second answer to this question C.V. Hansen says: "[...] sometimes the overhead of lazyness can be too much or wasteful". Is that supposed to mean that it is used to save memory (saving the value is cheaper than saving the expression)?

An explanation and an example would be great!

Thanks!

[EDIT] I think I should have chosen an example without {-# UNPACK #-}. So let me make one myself. Would this ever make sense? Is yes, why and in what situation?

data MyType = Const1 !Int
            | Const2 !Double
            | Const3 !SomeOtherDataTypeMaybeMoreComplex
like image 699
o1iver Avatar asked Jun 03 '11 19:06

o1iver


People also ask

What is strictness in Haskell?

Function argumentsA function is considered strict in one of its arguments if, when the function is applied to a bottom value for that argument, the result is bottom. As we saw way above, + for Int is strict in both of its arguments, since: undefined + x is bottom, and x + undefined is bottom.

Are Haskell functions strict?

Haskell is a non-strict language, and most implementations use a strategy called laziness to run your program.


1 Answers

The goal here is not strictness so much as packing these elements into the data structure. Without strictness, any of those three constructor arguments could point either to a heap-allocated value structure or a heap-allocated delayed evaluation thunk. With strictness, it could only point to a heap-allocated value structure. With strictness and packed structures, it's possible to make those values inline.

Since each of those three values is a pointer-sized entity and is accessed strictly anyway, forcing a strict and packed structure saves pointer indirections when using this structure.

In the more general case, a strictness annotation can help reduce space leaks. Consider a case like this:

data Foo = Foo Int

makeFoo :: ReallyBigDataStructure -> Foo
makeFoo x = Foo (computeSomething x)

Without the strictness annotation, if you just call makeFoo, it will build a Foo pointing to a thunk pointing to the ReallyBigDataStructure, keeping it around in memory until something forces the thunk to evaluate. If we instead have

data Foo = Foo !Int

This forces the computeSomething evaluation to proceed immediately (well, as soon as something forces makeFoo itself), which avoids leaving a reference to the ReallyBigDataStructure.

Note that this is a different use case than the bytestring code; the bytestring code forces its parameters quite frequently so it's unlikely to lead to a space leak. It's probably best to interpret the bytestring code as a pure optimization to avoid pointer dereferences.

like image 89
bdonlan Avatar answered Oct 06 '22 23:10

bdonlan