1

I have the following Haskell type definition:

import Data.Sequence(Seq, length)
import Data.ByteString.UTF8(ByteString)

type StringSeq = Seq ByteString

I have expressions of type StringSeq for which I would like to force strict evaluation with deepseq. So I need to define instances of NFData. I did the following:

import Control.DeepSeq(NFData, deepseq)

instance NFData ByteString

instance NFData a => NFData (Seq a) where
  rnf s = rnf (length s)

So I compute the length of a sequence to force evaluation of the sequence itself. This seems to work but is this the correct implementation? Are there better ones? Does computing the length of a sequence introduce too much overhead?

Petr
  • 62,528
  • 13
  • 153
  • 317
Giorgio
  • 5,023
  • 6
  • 41
  • 71
  • 3
    Computing the length of a `Seq` will do nothing useful, because a `Seq` is already spine-strict. It's certainly not a valid `NFData` instance, because it doesn't even try to force the contents of the `Seq`. Also, it's pretty unlikely that `NFData` is actually what you want; what is it you're really trying to do? `deepseq` is usually "too much, too late" for getting the right strictness behavior (and performance). – shachaf Dec 19 '12 at 23:51
  • Apart from what shachaf said, there is already an `NFData` instance : `instance NFData a => NFData (Seq a) where rnf (Seq xs) = rnf xs`. (arrh, only in the `containers` coming with 7.6.1, not for the earlier versions) – Daniel Fischer Dec 19 '12 at 23:53
  • 1
    But, all you have to do is `seq` the `ByteString`s before you put them in the `Seq`. Due to the spine strictness of `Seq`, that ensures complete evaluation. – Daniel Fischer Dec 19 '12 at 23:59
  • @Daniel Fischer: But the `Seq` is the result of a function: the function application returning the `Seq` must also be evaluated. I have to check if `seq` is enough here, maybe not? – Giorgio Dec 20 '12 at 06:59

2 Answers2

1

You can define a monad for strictness

data Strict a = Strict {fromStrict :: !a}
instance Monad Strict where
   return = Strict
   (Strict x) >>= f = f x

Okay, I don't think this actually obeys the monad laws, but it is close enough. Using this you can define a function

srnf = Strict . rnf 

such that

instance NFData a => NFData (Seq a) where
  rnf s = fromStrict $ (mapM srnf s) >> return ()

Untested, but should work (and it should work for all Traversable data structures).

Philip JF
  • 28,199
  • 5
  • 70
  • 77
  • I'm just curious, isn't `newtype Strict a = Strict { fromStrict :: a }` strict as well, with slightly better performance? – Petr Dec 21 '12 at 07:15
  • `newtype` isn't strict. It just does not exist. The point is that the pattern match in the definition of `>>=` forces evaluation when the identity monad does not. You could use the newtype you defined, but you would have to say `Strict x >>= f = seq x $ f x`. – Philip JF Dec 21 '12 at 09:14
1

Computing length is not enough, you need to compute the normal forms of the content of a sequence. I suggest you to use seqFoldable from Control.Seq which allows you to force any foldable structure. Then you can call simply

mySeq `using` seqFoldable rdeepseq

or define

instance NFData a => NFData (Seq a) where
    rnf = seqFoldable rdeepseq
Petr
  • 62,528
  • 13
  • 153
  • 317