How can we make a user pass an eventHandler, which uses a stateMonad but is invoked in a separate thread? For example the in the following example, how should the forkIO be called so that eventHandler can invoke operate? I am new to Haskell, please correct me if this is a wrong api to expose to users?
data MyTypeResult a = MyTypeValue a
data MyTypeState = MyTypeState {_counter :: Int}
newtype MyType a = MyType {
unMyType :: StateT MyTypeState IO (MyTypeResult a)
}
instance Monad MyType where
(>>=) = myTypeBind
return = myTypeReturn
fail = myTypeFail
myTypeBind = undefined
myTypeReturn = undefined
myTypeFail = undefined
type Event = String
type Handler = Event -> MyType ()
doSomethingAwesome :: MyType Event
doSomethingAwesome = undefined
operate :: String -> MyType ()
operate = undefined
start :: Handler -> MyType ()
start h = do
event <- doSomethingAwesome
--forkIO $ h event -- The line that is troubling
return ()
testHandler :: Event -> MyType()
testHandler _ = operate "abcd"
myMain = start testHandler
You cannot have a State computation running in multiple threads sharing the same state because behind the scenes, the State monad is nothing more than a chain of function calls that passes the state value on to the next function in the chain.
For multithreaded code you can replace StateT s IO with ReaderT (IORef s) IO and use
forkIO $ runReaderT (h event) stateVar
to fork new threads (where stateVar is an IORef that contains the shared state).
Inside the ReaderT stack, you read the current shared state with
stateVar <- ask
s <- lift $ readIORef stateVar
and update it with
stateVar <- ask
lift $ atomicModifyIORef stateVar f
where f is a pure function that takes the current state and returns the modified state plus an auxiliary result.
If you need anything more fancy (e.g. modify the state using monadic functions), then you should use either MVar or TVar instead of IORef.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With