I am looking for a Haskell design to put together a chain of monadic actions (usually IO) in such a way that the next steps depend on the previous ones, but in some cases can be performed before they are completed.
The solution I came up with so far:
type Future ma = m (ma)
Read: monadic action, which starts a certain process and returns an action that will return the result of this process (possibly waiting for the completion of this process).
So, in some chain a >>= b >>= c
b receives the action returned as a result. If b evaluates this action, it waits for completion, otherwise it will be executed in parallel. It also means that if an action does not require the result of the previous one as an argument, it does not depend on it by definition, so the dependencies are explicit.
Code example:
date :: Future IO String -- long process to find out the date date = do print "attempting to get date" -- will usually start some thread or process to compute the date return (print "today") -- will wait for this thread or process and return the computed date main = do d <- date -- starts recieving the date print "foo" -- some other process d >>= print -- waits until the date has been computed and prints it out
Output:
"attempting to get date" "foo" "today"
A problem arises: if an action decides to wait for the previous one, it will always depend on everyone else until (in my case). But in the above example, if c decides to wait for b, but b did not decide to wait for a, c can start before completion, which should not happen.
As a solution, I wrote another combination operator:
(>=>) :: Monad m => Future ma -> (ma -> Future mb) -> Future mb a >=> f = do r1 <- a r2 <- f r1 return (r1 >> r2)
So, this will combine the “wait actions”, and a >=> b >=> c
will work fine, if c waits for b, this wait action will also wait. However, there is another problem with this approach (in addition, you need to remember that you need to use> => instead of → =): wait actions can be evaluated many times. If b waits for a and c waits for b, wait b will be connected to wait, however, and therefore wait a will be executed twice.
The actual problem is in> =>: f r1
can evaluate r1 when the return statement does not need to be sequenced with r2 (since it has already been executed and thus finished). But it also cannot be, I do not know.
So I basically want this, but without the ability to trigger wait actions several times. Unfortunately, I am not very versed in functional design.
So, I hope that you can somehow illuminate me, how to increase or change my design or point to another, more flexible approach.
Change In accordance with the answers so far, I would like to give some clarification on what I really want:
I do not want to delay (or even skip) the execution of actions, I also do not need threads or similar parallelism functions. Actually, I call external processes. Example:
backup :: Future IO ExitCode backup = do pid <- startProcess "backup" return (waitForProcessAndGetExitCode pid)
When I now perform actions like backup >=> otherAction
, otherAction can be triggered during backup (which saves a lot of time in general). But otherAction may require the completion of the backup, in which case it can use its parameter to wait for the backup and check if it was successful. In any case, the backup should be performed.
Now I am looking for a good general solution, ideally not tied to the IO monad.
Update I found a solution that worked for me. I described this in a separate answer below.