【问题标题】:How to fork inside monad transformer如何在单子变压器内部分叉
【发布时间】:2012-03-06 13:23:27
【问题描述】:

考虑一些 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 上下文中的单独线程内查看和打印 quuxes

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

这显然行不通。我想有什么办法可以解决这么简单的问题,就是想不通。

【问题讨论】:

    标签: haskell monad-transformers


    【解决方案1】:

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

    forkJ :: J () -> J ThreadId
    

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

    但是,如果您愿意稍微重构您的逻辑,一个可能适合您的选项(如果您要寻找的只是一个单独的线程,可以在您发出分叉时访问原始状态)是lifted-base pakcage,它依赖于 monad-control。只要您的转换器堆栈底部有 IO,它基本上就会为您提供上面的 forkJ 函数。

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

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

    【讨论】:

    • 好的,我去看看lifted-base。谢谢。
    【解决方案2】:

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-01-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-03
      • 2014-08-03
      • 2022-11-09
      相关资源
      最近更新 更多