I'm trying to figure out a good way to implement a request response pattern, where a monad can request the monad runner to perform an action and return a value back to the monad.
The reason I want to do this is because I have a bunch of tasks to perform where some of the work is IO based and some is CPU based. I want a bunch of cpu threads to do the cpu work, hand off io tasks to another thread designated to do disk work, and be then free to work on other CPU tasks while the disk thread finds a value for them. A task might be something like:
do some cpu work
request load a value from disk
do some more cpu work
request another value from disk
... etc ..
I created the following as a simple way to do this, where ReqRes, below, represent the disk based tasks. However, in testIO, it has a waterfall look to it where the code marches off to the right, every time it makes a new request, due to nested functions.
I was wondering if there is a cleaner way to do it, that doesn't require this nested function structure.
module ReqResPattern where
import Control.Monad.IO.Class (MonadIO(..))
data ReqRes m = RR1 String (String -> m (ReqRes m)) | RR2 Int (Int -> m (ReqRes m)) | Fin
testIO :: MonadIO m => m (ReqRes m)
testIO =
do
return $ RR1 "fred"
(\x ->
do
liftIO $ putStrLn $ "str: " ++ x
return $ RR2 1
(\y ->
do
liftIO $ putStrLn $ "int: " ++ (show y)
return $ Fin
)
)
runTestIO :: IO ()
runTestIO =
doit testIO
where
doit :: IO (ReqRes IO) -> IO ()
doit m =
do
v <- m
case v of
RR1 v f -> doit $ f (v ++ " foo")
RR2 v f -> doit $ f (v+1)
Fin -> return ()
return ()