forkIO accepts an
IO computation only,
and requires the caller to reconstruct the full monadic stack by
hand in the new thread. In contrast, this module's
forkIO runs a
computation in the same monad as the parent thread, transparently
transplanting the monad stack to the new thread.
For example, the following code which uses
type MyMonad = ReaderT Int (StateT String IO) forkAndDo :: MyMonad ThreadId forkAndDo = do r <- ask s <- lift get liftIO $ forkIO $ (runStateT (runReaderT forkedDo r) s >> return ()) forkedDo :: MyMonad () forkedDo = liftIO $ putStrLn "forkedDo running"
can be reexpressed with this module's
type MyMonad = ReaderT Int (StateT String IO) forkAndDo :: MyMonad ThreadId forkAndDo = forkIO forkedDo forkedDo :: MyMonad () forkedDo = liftIO $ putStrLn "forkedDo running"
forkIO can operate on any monad that is an instance of
ForkableMonad instances are defined for
StateT, as well as
IO. Here is the precise
meaning of "transplant" for each of these:
IOrequires no special work, since
forkIOalready provides the "transplanting" of an
IOaction to a new thread.
ReaderTmakes the parent thread's environment available for consultation in the new thread.
StateTmakes a copy of the parent thread's state available in the new thread. The states in the two threads are not linked, so it is expected that they will diverge as each thread updates its own copy of the state.
Other standard transformers (notably
RWST) do not have an instance defined, because those instances
can only be defined through data loss.
For example, the current output of a
Writer cannot be accessed
from within the monad, so the best that can be done is to create a
Writer state for the new thread, and to discard all
data written in that thread when the thread terminates.
If you want to use
forkIO on a monad stack that includes one of
these lossy monads, you will need to define the
instances yourself. The same goes for any custom monads you may
have in the stack.
This module reexports
Control.Concurrent overlayed with the
forkIO, so you can simply change your import from
Control.Concurrent.Forkable to use this
forkIO in your existing concurrent code.
Spark off a new thread to run the monadic computation passed
as the first argument, and return the
ThreadId of the newly
The new thread will run the computation using the same monadic context as the parent thread.
As a convenience, this forkIO accepts a computation returning any value, not just unit. This value is discarded when the computation terminates.