如何在monad变压器内部分叉

时间:2012-03-06 13:23:28

标签: haskell monad-transformers

考虑一些monad变换器堆栈,比如说

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
...
newtype J = J { runJ :: ErrorT Foo (StateT Bar IO) a } deriving (Applicative, Functor, etc)

J中的一些函数:

peekNextQuux :: J Quux
peekNextQuux = ...

withJ :: J a -> IO (Either Foo a)
withJ = ...

然后我发现自己在J上下文中。我可以写

f = withJ $ peekNextQuux >>= liftIO . print

现在我想在J context

中的单独线程中查看并打印quuxes
g = withJ . liftIO . forkIO . forever $ peekNextQuux >>= liftIO . print

这显然不起作用。我想有一些方法可以解决这么简单的问题,只是想不出来。

2 个答案:

答案 0 :(得分:9)

我不确定这是否是你需要的,但听起来你正在寻找一个功能

forkJ :: J () -> J ThreadId

与forkIO类似,但在J上下文中工作。一般来说,dflemstr的所有点都是有效的。由于Haskell的纯度,有许多关于状态管理的未解决的问题。

但是,如果您愿意稍微重构一下逻辑,那么一个可能对您有用的选项(如果您要查找的是一个单独的线程,当您发出fork时可以访问原始状态)是lifted-base pakcage,取决于monad-control。只要在变换器堆栈的底部有IO,它基本上就会给你上面的forkJ函数。

现在,如果您希望2个线程以有状态的方式进行通信,以便子进程中引发的错误作为ErrorT机制的一部分传播到主线程,这是不可能的(如dflemstr所述)。但是,您可以使用Control.Concurrent模块系列中的构造在2个线程之间建立通信通道。以下模块之一可能具备您所需的功能:

Control.Concurrent.Chan
Control.Concurrent.MVar
Control.Concurrent.STM

答案 1 :(得分:8)

您认为它如何运作?单独的线程必须能够访问某些状态和一些错误处理,因为J包裹StateTErrorT。线程应该如何访问它?在新线程中更新状态时,是否应该在旧线程中更改?当新线程抛出异常时,旧线程是否应该停止?

它无法工作,因为StateTErrorT是纯monad变换器,所以我描述的行为无法实现。您必须将状态显式传递给新线程并在那里运行新的状态monad才能使其工作:

g = withJ . ... $ do
  state <- get
  liftIO . forkIO $ do
    flip execStateT state . forever $ peekNextQuux >>= liftIO . print