10

考虑一些单子变压器堆栈,比如说

{-# 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现在我想在上下文中的单独线程中查看和打印 quuxes

g = withJ . liftIO . forkIO . forever $ peekNextQuux >>= liftIO . print

这显然行不通。我想有一些方法可以解决这样一个简单的问题,只是想不通。

4

2 回答 2

10

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

forkJ :: J () -> J ThreadId

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

但是,如果您愿意稍微重构您的逻辑,那么一个可能对您有用的选项(如果您正在寻找的是一个单独的线程,可以在您发出叉子时访问原始状态)是提升 -基础包,这取决于单子控制。只要您在变压器堆栈的底部有 IO,它就会基本上为您提供上面的 forkJ 功能。

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

Control.Concurrent.Chan
Control.Concurrent.MVar
Control.Concurrent.STM
于 2012-03-06T16:57:33.567 回答
9

你期望它如何工作?单独的线程必须能够访问一些状态和一些错误处理,因为JwrapsStateTErrorT. 线程应该如何访问这个?当状态在新线程中更新时,它是否也应该在旧线程中更改?当新线程抛出异常时,旧线程是否应该停止?

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

g = withJ . ... $ do
  state <- get
  liftIO . forkIO $ do
    flip execStateT state . forever $ peekNextQuux >>= liftIO . print
于 2012-03-06T14:25:16.897 回答