Search code examples
haskellconcurrencyparallel-processingstrictness

Is evaluate or $! sufficient to WHNF-force a value in a multithreaded monadic context, or do I need pseq?


The following seems to work (as in: it keeps saying Surely tomorrow every second)

import Control.Concurrent
import Control.Concurrent.MVar

import Control.Exception (evaluate)

main :: IO ()
main = do
    godot <- newEmptyMVar
    forkIO $ do
        g <- evaluate $ last [0..]
        putMVar godot g
    let loop = do
        threadDelay $ 10^6
        g <- tryTakeMVar godot
        case g of
            Just g -> return ()
            Nothing -> putStrLn "Surely tomorrow." >> loop
    loop

This uses evaluate to ensure last [0..] is actually forced to WHFN before filling the MVar – if I change the forked thread to

    forkIO $ do
        let g = last [0..]
        putMVar godot g

then the program terminates.

However, evaluate uses seq. In the context of deterministic parallelism, it's always emphasized that seq is not sufficient to actually guarantee evaluation order. Does this problem not arise in a monadic context, or should I better use

    forkIO $ do
        let g = last [0..]
        g `pseq` putMVar godot g

to ensure the compiler can't reorder the evaluation so tryTakeMVar succeeds prematurely?


Solution

  • The point of pseq is to ensure that after the parent thread sparks a computation with par, it does not immediately proceed to try to evaluate the result of the sparked computation itself, but instead does its own job first. See the documentation for an example. When you're working more explicitly with concurrency, you shouldn't need pseq.