I am looking for a Haskell design to compose a chain of monadic actions (usually IO) in a manner, that later actions are dependent on previous ones but in some cases can be executed before they have finished.
The solution I came up with so far is:
type Future m a = m (m a)
Read: a monadic action, which starts some process and returns an action which will return the result of that process (possibly by waiting for this process to finish).
So in some chain a >>= b >>= c
b gets an action returning a’s result. If b evaluates this action it waits for a to finish, otherwise it will be run in parallel. That also means that if some action does not require the result of the previous one as argument, it does not depend on it by definition, so the dependencies are explicit.
Some example code:
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"
There is a problem through: if an action decides to wait for the previous one it will always be dependent on all the others before (in my case). But in the example above if c decides to wait for b but b did not decide to wait for a, c may start before a has finished, which should not happen.
As a solution I wrote another combining operator:
(>=>) :: Monad m => Future m a -> (m a -> Future m b) -> Future m b
a >=> f = do
r1 <- a
r2 <- f r1
return (r1 >> r2)
So this will combine the “wait actions” and a >=> b >=> c
will work just fine, if c waits for b this wait action will also wait for a.
However there is another problem with this approach (apart from that you need to remember to use >=> instead of >>=): the wait actions may be evaluated many times.
If b waits for a and c waits for b the wait for b will be connected to the wait for a nevertheless and thus the wait for a will be executed twice.
The actual problem is in >=>: f r1
may evaluate r1 in wich case it does not need to be sequenced with r2 in the return statement (as it already was executed and thus a has finished). But it also might not, I cannot know.
So what I basically want is exactly this but without the possibility to run the wait actions several times. Unfortunately I am not very experienced in functional design.
So I hope you can enlighten me in some way how to augment or change my design or point me to a different, more flexible approach.
Edit According to the answers so far I like to give some more clarification about what I actually want:
I do not want to defer (or even skip) the execution of actions, neither do I require threads or similar parallelism features. Actually I am calling external processes. An example would be
backup :: Future IO ExitCode
backup = do
pid <- startProcess "backup"
return (waitForProcessAndGetExitCode pid)
When I now chain actions like backup >=> otherAction
, otherAction can run while the backup is running (which saves much time overall). But otherAction may require the backup to be completed, in which case it can use its parameter to wait for the backup and to check whether it was successful. Either way the backup has to be executed.
I am now looking for a nice general solution, ideally not tied to the IO monad.
Update I found a solution that worked for me. I described it in a seperate answer below.