考虑一些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
g = withJ . liftIO . forkIO . forever $ peekNextQuux >>= liftIO . print
这显然不起作用。我想有一些方法可以解决这么简单的问题,只是想不出来。
答案 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
包裹StateT
和ErrorT
。线程应该如何访问它?在新线程中更新状态时,是否应该在旧线程中更改?当新线程抛出异常时,旧线程是否应该停止?
它无法工作,因为StateT
和ErrorT
是纯monad变换器,所以我描述的行为无法实现。您必须将状态显式传递给新线程并在那里运行新的状态monad才能使其工作:
g = withJ . ... $ do
state <- get
liftIO . forkIO $ do
flip execStateT state . forever $ peekNextQuux >>= liftIO . print