ForkIO与stateT

Aks*_*hat 2 haskell

我们如何让用户传递一个eventHandler,它使用stateMonad但是在一个单独的线程中调用?例如,在以下示例中,应该如何调用forkIO以便eventHandler可以调用操作?我是Haskell的新手,请纠正我,如果这是一个错误的API暴露给用户?

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
Run Code Online (Sandbox Code Playgroud)

sha*_*ang 5

你不能State在多个共享相同状态的线程中运行计算,因为在幕后,Statemonad只不过是一个函数调用链,它将状态值传递给链中的下一个函数.

对于多线程代码,你可以替换StateT s IO使用ReaderT (IORef s) IO,并使用

forkIO $ runReaderT (h event) stateVar
Run Code Online (Sandbox Code Playgroud)

叉新线程(其中,stateVar是IORef包含共享状态).

在ReaderT堆栈内部,您可以使用读取当前的共享状态

stateVar <- ask
s <- lift $ readIORef stateVar
Run Code Online (Sandbox Code Playgroud)

并用它更新

stateVar <- ask
lift $ atomicModifyIORef stateVar f
Run Code Online (Sandbox Code Playgroud)

where f是一个纯函数,它接受当前状态并返回修改后的状态加上一个辅助结果.

如果你需要更多花哨的东西(例如使用monadic函数修改状态),那么你应该使用MVar或TVar代替IORef.