Search code examples
haskellasynchronouslazy-evaluationghc

How to enforce full evaluation inside async?


I am playing with async library and trying to figure out its API in practice. I've noticed a strange behavior I didn't expect. It looks like a bug, but maybe it's a feature and I just need to know a workaround.

import Control.Concurrent
import Control.Concurrent.Async

> withAsync (putStrLn "HELLO") (\_ -> putStrLn "WORLD")
WORLHEDL

The snippet above is working just fine - both line lines executed, but more complex async body is evaluated partially.

> withAsync (putStrLn "XXXXXXXXXX" >> putStrLn "HELLO") (\_ -> putStrLn "WORLD")
WOXRXLXDX

See, the second putStrLn is not executed. I guess I need to wrap the whole async body in some sort of bnf, but it looks weird anyway. Why withAsync doesn't do that for me?


forkIO works just right, but I don't want to bother with unlifting. async has pair library unlift-async which propagates monad automatically.

forkIO (Prelude.putStrLn "XXXXXXXXXX" >> Prelude.putStrLn "HELLO")
ThreadXIXdX X1X1X3X
XXX
HELLO

I found lifted-base with fork function. It works as forkIO and pass parent monad to child thread.


Solution

  • withAsync interrupts the forked thread (1st argument) when the main thread (2nd argument) terminates. This is explicit in the documentation of withAsync:

    When the function returns or throws an exception, uninterruptibleCancel is called on the Async.

    (...) This is a useful variant of async that ensures an Async is never left running unintentionally.

    If you want to just fork a thread, use async.

    async (putStrLn "XXXXXXXX" >> putStrLn "WORLD") >> putStrLn "HELLO"