首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >带有ForkIO的stateT

带有ForkIO的stateT
EN

Stack Overflow用户
提问于 2013-08-14 14:02:27
回答 3查看 747关注 0票数 3

如何让用户传递一个eventHandler,它使用一个stateMonad,但在一个单独的线程中被调用?例如,在下面的示例中,如何调用forkIO以使eventHandler能够调用操作?我是新来的Haskell,请纠正我,如果这是一个错误的api公开给用户?

代码语言:javascript
复制
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
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-08-14 15:25:59

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

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

代码语言:javascript
复制
forkIO $ runReaderT (h event) stateVar

分叉新线程(其中stateVar是包含共享状态的IORef )。

ReaderT堆栈中,使用

代码语言:javascript
复制
stateVar <- ask
s <- lift $ readIORef stateVar

并用

代码语言:javascript
复制
stateVar <- ask
lift $ atomicModifyIORef stateVar f

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

如果您需要更多花哨的东西(例如使用一元函数修改状态),那么您应该使用MVarTVar而不是IORef

票数 6
EN

Stack Overflow用户

发布于 2013-08-14 17:41:44

诚然,您不能让多个StateT s IO操作在多个线程中运行,它们都共享相同的s状态,但是您可以让一个StateT s IO在一个线程中运行,它的状态与使用monad-controllifted-base的其他线程隔离。

我最近偶然发现了这些包裹,真是太棒了。以下是一个简单的例子

代码语言:javascript
复制
{-# 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操作。

票数 2
EN

Stack Overflow用户

发布于 2013-08-14 16:31:55

forkIO不会在MyType内部工作,除非您通过为MyType实现MonadIO类来使用liftIO,否则可以使MyType成为一个类型的同义词,并这样做:

代码语言:javascript
复制
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 testHandler
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/18233902

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档