如何让用户传递一个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发布于 2013-08-14 15:25:59
您不能让State计算在共享相同状态的多个线程中运行,因为在幕后,State monad只不过是将状态值传递给链中的下一个函数的一系列函数调用。
对于多线程代码,可以将StateT s IO替换为ReaderT (IORef s) IO并使用
forkIO $ runReaderT (h event) stateVar分叉新线程(其中stateVar是包含共享状态的IORef )。
在ReaderT堆栈中,使用
stateVar <- ask
s <- lift $ readIORef stateVar并用
stateVar <- ask
lift $ atomicModifyIORef stateVar f其中,f是一个纯函数,它接受当前状态并返回修改后的状态加上一个辅助结果。
如果您需要更多花哨的东西(例如使用一元函数修改状态),那么您应该使用MVar或TVar而不是IORef。
发布于 2013-08-14 17:41:44
诚然,您不能让多个StateT s IO操作在多个线程中运行,它们都共享相同的s状态,但是您可以让一个StateT s IO在一个线程中运行,它的状态与使用monad-control和lifted-base的其他线程隔离。
我最近偶然发现了这些包裹,真是太棒了。以下是一个简单的例子
{-# LANGUAGE FlexibleContexts #-}
import Control.Concurrent.Lifted
import Control.Monad.Base
import Control.Monad.Trans.Control
import Control.Monad.State
t :: IO Int -> StateT Int IO ()
t io = replicateM_ 10 $ do
x <- get
y <- liftIO io
liftIO $ print x
put $ x + y
async :: MonadBaseControl IO m => (IO a -> m ()) -> m (a -> IO ())
async thread = do
mvar <- liftBase newEmptyMVar
fork $ thread (takeMVar mvar)
return $ liftBase . putMVar mvar
main :: IO ()
main = evalStateT (async t) 0 >>= forM_ [1..10]基本上,只要您的monad是IO上的转换器堆栈,您就可以“在”转换器堆栈中分叉、处理异常或任何其他特定的IO操作。
发布于 2013-08-14 16:31:55
forkIO不会在MyType内部工作,除非您通过为MyType实现MonadIO类来使用liftIO,否则可以使MyType成为一个类型的同义词,并这样做:
data MyTypeResult a = MyTypeValue a
data MyTypeState = MyTypeState {_counter :: Int}
type MyType a = StateT MyTypeState IO (MyTypeResult a)
type Event = String
type Handler = Event -> MyType ()
doSomethingAwesome :: MyType Event
doSomethingAwesome = undefined
operate :: String -> MyType ()
operate = undefined
start :: Handler -> MyType ()
start h = do
MyTypeValue event <- doSomethingAwesome
st <- get
liftIO $ forkIO $ runStateT (h event) st >> (return ())
return $ MyTypeValue ()
testHandler :: Event -> MyType()
testHandler _ = operate "abcd"
myMain = start testHandlerhttps://stackoverflow.com/questions/18233902
复制相似问题