如何在monad变压器内部分叉

Mat*_*nov 9 haskell monad-transformers

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

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

以及一些功能J:

peekNextQuux :: J Quux
peekNextQuux = ...

withJ :: J a -> IO (Either Foo a)
withJ = ...
Run Code Online (Sandbox Code Playgroud)

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

f = withJ $ peekNextQuux >>= liftIO . print
Run Code Online (Sandbox Code Playgroud)

现在我想在J上下文中的单独线程中查看和打印quuxes

g = withJ . liftIO . forkIO . forever $ peekNextQuux >>= liftIO . print
Run Code Online (Sandbox Code Playgroud)

这显然是行不通的.我想有一些方法可以解决这么简单的问题,只是想不出来.

oza*_*man 9

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

forkJ :: J () -> J ThreadId
Run Code Online (Sandbox Code Playgroud)

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

但是,如果您愿意稍微重构一下逻辑,那么可能适用于您的一个选项(如果您正在寻找的是一个单独的线程,当您发出分支时可以访问原始状态)是被解除的 -基础 pakcage,这取决于monad控制.只要在变换器堆栈的底部有IO,它基本上就会给你forkJ函数.

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

Control.Concurrent.Chan
Control.Concurrent.MVar
Control.Concurrent.STM
Run Code Online (Sandbox Code Playgroud)


dfl*_*str 8

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

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

g = withJ . ... $ do
  state <- get
  liftIO . forkIO $ do
    flip execStateT state . forever $ peekNextQuux >>= liftIO . print
Run Code Online (Sandbox Code Playgroud)