2

Given the following functions for evalPair, parPair and deepSeq respectively,

evalPair :: Strategy a -> Strategy b -> Strategy (a,b)
evalPair sa sb (a,b) = do
      a' <- sa a
      b' <- sb b
      return (a',b')

parPair :: Strategy a -> Strategy b -> Strategy (a,b)
parPair sa sb = evalPair (rparWith sa) (rparWith sb)

rdeepseq :: NFData a => Strategy a
rdeepseq x = rseq (force x)

The question is why will the following code be evaluated in parallel as the author claims?

parPair rdeepseq rdeepseq :: (NFData a, NFData b) => Strategy (a,b)

I am in dount since Simon Marlow says the following

To break down what happens when this Strategy is applied to a pair: parPair calls evalPair, and evalPair calls rparWith rdeepseq on each component of the pair. So the effect is that each component will be fully evaluated to normal form in parallel.

According to what I see rparWith rdeepseq is informally rpar . rseq . force which means the argument will be evaluated to normal form, which it will be evaluated with rseq sequentially, and hence in the end the rpar is of no use in a parallel context.

Everything is taken from Simon Marlow's book.


Thanks in advance.

McGill
  • 49
  • 5

1 Answers1

4

Let's have a look at parPair rdeepseq rdeepseq:

parPair rdeepseq rdeepseq (a,b)
 = evalPair (rparWith rdeepseq) (rparWith rdeepseq) (a,b)
 = do
    a' <- rparWith rdeepseq a
    b' <- rparWith rdeepseq b
    return (a', b')

Since rparWith sparks it arguments, rdeepseq a will be evaluated in parallel (possibly). It wouldn't be in parallel, if you were to use

evalPair rdeepseq rdeepseq

instead. The rparWith is essential, as it sparks its argument for evaluation with the given strategy.

Zeta
  • 103,620
  • 13
  • 194
  • 236
  • So your answer would mean that my understanding of strategy composition was incorrect (i.e rparWith rdeepseq == rpar . rseq . force is basically incorrect)? BTW thanks for the answer. – McGill Jan 26 '17 at 06:04
  • 1
    @McGill: no, that's completely fine (well, except for the missing `dot`, since you cannot compose strategies with `(.)`) But `(.) f g x` is `f (g x)`, so `(rpar . rseq. force) x` is `rpar (rseq (force x))`. In other programming languages, we would have to evaluate `force x` to use `rseq`. But since Haskell isn't strict, we don't actually evaluate the arguments in `rpar `. Instead, we hand `` over to `rpar`, which _then_ sparks `` for evaluation in parallel. – Zeta Jan 26 '17 at 07:53