Reputation: 931
I have a function that takes some time to process some input.
I want to make it so that if a user calls that function it would start doing what it does, but if the user calls it again and if it's still working on the thing, it'll return a message telling user to sit tight.
runService :: Arg1 -> Arg2 -> IO String
runService arg1 arg2 = do
isRunning <- {- Check if the function is running -}
isDone <- {- Check if the function is done working -}
if isRunning
then return "Work is in progress!"
else if isDone
then return "Work is done."
else do startService arg1 arg2
return "Work has been started."
I believe I'll need to modify startService
function as well but I'm not sure how.
This is its current type signature.
startService :: Arg1 -> Arg2 -> IO ()
It would be even more useful if runService
could provide a "progress bar" of sorts.
runService :: Arg1 -> Arg2 -> IO String
runService arg1 arg2 = do
isRunning <- {- Check if the function is running -}
isDone <- {- Check if the function is done working -}
if isRunning
then {- Return some progress indicator -}
else if isDone
then return "Work is done."
else do startService arg1 arg2
return "Work has been started."
{- Return some progress indicator -}
It is fairly simple for startService
to print its status using putStrLn
but I'm not certain how I'd supply these status strings to runService
or how I'd thread this status upwards all the way to main
.
For a procedural language, this would call for a global variable. Looking for something similar in Haskell led me to StateT, ReaderT, WriterT
monad transformers but I am having a hard time understanding them and their usage in general and in this perticular context.
Upvotes: 3
Views: 239
Reputation: 767
The type of design approach you propose looks heavily influenced by imperative programming background. Be careful with that, because although you can do imperative programming in Haskell, and mimic some common imperative patterns and behavior, going this way would be full of frustration, with no real benefit.
So you want to take a functional look at it :
Basically, what you can do is share a concurrency primitive (think of a mutable box reference) between your computing thread and your observing thread. This is enough to implement common behavior, like synchronization (locking observing thread until computation finish and pass result) or waiting (is the computation done ? yes / no).
Observing progress is another matter involving cooperation from the computing function. If you can divide work in steps, you should probably use a continuation passing style so that you can keep your function pure. This function would do one step of a computation, then return a couple (estimated progress ration, next step).
An other function, this one in IO, would just keep calling the computation function and update the shared state with progress ratio, until work is done.
Inside the shared box, use an Algebraic Data Type :
data ComputationState result = Working Float -- progress ratio
| Done result -- done, get the result
Here you go.
Upvotes: 1