I’m trying to use concurrency in Haskell for a specific optimization in which only one of two values is required, and depending on the situation, either one may be much faster to create than the other.
I thought I could just run 2 threads with forkIO, and then wait until a value is placed in an MVar. Here is a simple Test i have written for this:
import Control.Concurrent
main = do out <- newEmptyMVar
t1 <- forkIO (makeString out)
t2 <- forkIO (makeInt out)
v <- takeMVar out
killThread t1
killThread t2
case v of
Left s -> putStrLn s
Right i -> putStrLn $ show i
makeString out = do s <- return ( show (primes !! 10000))
putMVar out $ Left s
makeInt out = do i <- return 2
putMVar out $ Right i
primes = sieve [2..]
where sieve (x:xs) = x : (sieve $ filter ((/=0).(flip mod x)) xs)
Compiled with:
ghc --make -threaded Test
However, only the Left s case is ever reached, although getting the prime should take long enough for the makeInt thread to start (and return 2 really shouldn’t take that much time). Why is that, and how do I fix this?
The problem here is laziness. The
makeStringis just inserting a thunk to calculateshow (primes !! 10000), which then gets evaluated by the main thread afterwards. Inserting the thunk is quite fast, so it happens to win the race in this case.To force evaluation to happen within the thread, you can change
returntoevaluate:This should cause
makeIntto win the race in most cases (although it’s not guaranteed).