Haskell: Atomic IO wrapper/laziness?
concurrency, haskell
Solution
Where you have
writeIORef myRef newValue
Replace that with
newValue `par` writeIORef myRef newValue
That will spark off a thread to evaluate `newValue` in the background...
...with the caveat that it will only evaluate it to WHNF (basically, only the first level of the data structure will be evaluated). So an `Int` would be entirely evaluated, but a `String` wouldn't. A `Maybe a` value would be entirely evaluated to `Nothing` or partially evaluated to `Just _`.
So if you are using a more complex data structure, you will need to use `force` from Control.DeepSeq in the deepseq package, which will completely evaluate the value.
force newValue `par` writeIORef myRef newValue
If you want to use `modifyIORef`, I don't think you can reuse `modifyIORef` directly, but you can certainly define a similar function (`modifyIORef` is defined in terms of `readIORef` and `writeIORef` anyway):
modifyIORefInParallel :: NFData a => IORef a -> (a -> a) -> IO ()
modifyIORefInParallel ref f
= do old <- readIORef ref
let new = f old
force new `par` writeIORef ref new
(Note that if the last line was `force (f old) `par` writeIORef ref (f old)` it wouldn't work: the value that was forced in parallel would not be the value that was stored in the ref.)
(The `NFData a` restriction is from using `force`.)
Problem
I've wrote the following function that I believe should perform IO atomically (as long as everyone else is using the same `MVar`). ``` atomicIO :: MVar () -> IO a -> IO a atomicIO mvar io = do takeMVar mvar result <- io putMVar mvar () return result ``` Also, from what I understand, some parts of Haskell IO are lazy (e.g.. `IORef`s), so there is no need to actually perform the action in this section. It can just return a 'thunk' (is that the right word?) which lists the tasks that needs to be done. The idea is that, the critical section (i.e. the single threaded part) should be quite quick. However, lets say I'm writing to `IORef`s, and I want Haskell to start computing the result immediately, so it's ready to do when required. But like I said before, I don't want to get locked in the critical section when we're holding the `MVar` lock. So I've thought about doing this: ``` result <- io `par` io ``` or this ``` return result `par` result ``` or this ``` result `par` return result ``` But I'm not sure if this does the job. Is one of these the right approach, or is there another way? (my concern about the latter two is `IO ()` actions, as I figure evaluating `()` in parallel doesn't actually do any work).