MonadBaseControl:如何解除ThreadGroup

时间:2014-12-30 11:27:44

标签: haskell monad-transformers

在模块threadsControl.Concurrent.Thread.Group包中有一个函数forkIO

forkIO :: ThreadGroup -> IO α -> IO (ThreadId, IO (Result α))

我想使用monad-control中的MonadBaseControl来解除它。这是我的尝试:

fork :: (MonadBase IO m) => TG.ThreadGroup -> m α -> m (ThreadId, m (Result α))
fork tg action = control (\runInBase -> TG.forkIO tg (runInBase action))

这是错误消息:

Couldn't match type `(ThreadId, IO (Result (StM m α)))'
              with `StM m (ThreadId, m (Result α))'
Expected type: IO (StM m (ThreadId, m (Result α)))
  Actual type: IO (ThreadId, IO (Result (StM m α)))
In the return type of a call of `TG.forkIO'
In the expression: TG.forkIO tg (runInBase action)
In the first argument of `control', namely
  `(\ runInBase -> TG.forkIO tg (runInBase action))'

要使类型匹配需要更改的内容?

1 个答案:

答案 0 :(得分:2)

主要问题是IO a的{​​{1}}参数。要在forkIO中分配m a操作,我们需要一种方法来运行IOm a。要做到这一点,我们可以尝试制作具有IO a方法的monad类,但很少有趣的变换器可以提供。如果我们考虑例如runBase :: MonadBase b m => m a -> b a变换器,如果它首先有机会观察它自己的状态,那么它可以弄清楚如何在基础monad中用StateT运行某些东西。

runStateT

这表明类型为runFork :: Monad m => StateT s m a -> StateT s m (m b) runFork x = do s <- get return $ do (a, s') <- runStateT x s return a ,我们将在以下类型类中找到它。

runForkBase :: MonadBase b m => m a -> m (b a)

我在名称中添加了{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FunctionalDependencies #-} import Control.Monad.Base class (MonadBase b m) => MonadRunForkBase b m | m -> b where runForkBase :: m a -> m (b a) 这个词,以强调未来的状态变化通常不会在两个期货之间分享。出于这个原因,像Fork这样可以提供WriterT的少数有趣变换器只能提供无趣的runBase;它们产生的副作用永远不会被观察到。

对于runBase实例提供的有限降级形式的任何内容,我们都可以编写fork之类的内容。我从MonadRunForkBase IO m开始liftforkIO开始,而不是来自threads{-# LANGUAGE FlexibleContexts #-} import Control.Concurrent forkInIO :: (MonadRunForkBase IO m) => m () -> m ThreadId forkInIO action = runForkBase action >>= liftBase . forkIO ,你也可以这样做。

MonadRunForkBase

实例

这提出了一个问题,&#34;我们可以为&#34;提供哪些变形金刚MonadBase个实例?直接蝙蝠,我们可以为任何具有import Control.Monad.Trans.Identity import GHC.Conc.Sync (STM) instance MonadRunForkBase [] [] where runForkBase = return instance MonadRunForkBase IO IO where runForkBase = return instance MonadRunForkBase STM STM where runForkBase = return instance MonadRunForkBase Maybe Maybe where runForkBase = return instance MonadRunForkBase Identity Identity where runForkBase = return 个实例

的基础monad提供它们
import Control.Monad.Trans.Class

class (MonadTrans t) => MonadTransRunFork t where
    runFork :: Monad m => t m a -> t m (m a)

对于变形金刚来说,通常更容易构建这样的功能。这是变形金刚的类,它可以在直接的底层monad中运行一个分支。

runForkBaseDefault :: (Monad (t m), MonadTransRunFork t, MonadRunForkBase b m) =>
                      t m a -> t m (b a)
runForkBaseDefault = (>>= lift . runForkBase) . runFork

我们可以提供默认实现,以便在基础

中一直向下运行
MonadRunForkBase

这可让我们分两步完成StateT runFork个实例。首先,我们将使用上面的MonadTransRunFork制作import Control.Monad import qualified Control.Monad.Trans.State.Lazy as State instance MonadTransRunFork (State.StateT s) where runFork x = State.get >>= return . liftM fst . State.runStateT x 实例

MonadRunForkBase

然后我们将使用默认值来提供{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE UndecidableInstances #-} instance (MonadRunForkBase b m) => MonadRunForkBase b (State.StateT s m) where runForkBase = runForkBaseDefault 实例。

RWS

我们可以为import qualified Control.Monad.Trans.RWS.Lazy as RWS instance (Monoid w) => MonadTransRunFork (RWS.RWST r w s) where runFork x = do r <- RWS.ask s <- RWS.get return $ do (a, s', w') <- RWS.runRWST x r s return a instance (MonadRunForkBase b m, Monoid w) => MonadRunForkBase b (RWS.RWST r w s m) where runForkBase = runForkBaseDefault

做同样的事情
MonadRunForkBase

MonadBaseControl

与我们在前两部分中制定的MonadBaseControl不同,monad-control中的MonadBaseContol并未在假设中出现过&#34;未来的状态变化不会出现在一般在两个期货之间共享&#34;。 controlrestoreM :: StM m a -> m a努力通过forkIO恢复控制结构中的分支状态。这不会给forkIO基地带来问题;使用MonadBaseControlm (Result a)文档中提供的示例。这对于forkIO from threads来说只是一个小问题,因为返回了额外的m (Result a)

我们想要的IO (Result (StM m a))实际上会以IO的形式返回。我们可以删除m并将liftBase替换为m (Result (StM m a)),并将StM m a留给我们。我们可以将m a转换为恢复状态的a,然后使用restoreM返回Result ~ Either SomeException,但它会卡在Either l内。 restoreM是一个仿函数,因此我们可以在其中的任何位置应用m (Result (m a)),将类型简化为Either lTraversable也是Traversable,对于任何t Monad,我们始终可以在ApplicativesequenceA :: t (f a) -> f (t a)内与mapM进行交换。在这种情况下,我们可以使用特殊用途fmap,它是sequenceAMonad的组合,只有m (m (Result a))约束。这将提供m,并且>>=将通过Monad中的联接或仅使用{-# LANGUAGE FlexibleContexts #-} import Control.Concurrent import Control.Concurrent.Thread import qualified Control.Concurrent.Thread.Group as TG import Control.Monad.Base import Control.Monad.Trans.Control import Data.Functor import Data.Traversable import Prelude hiding (mapM) fork :: (MonadBaseControl IO m) => TG.ThreadGroup -> m a -> m (ThreadId, m (Result a)) fork tg action = do (tid, r) <- liftBaseWith (\runInBase -> TG.forkIO tg (runInBase action)) return (tid, liftBase r >>= mapM restoreM) 展平。这就产生了

m (Result a)

当我们在原始线程中运行Result时,它会将状态从分叉线程复制到原始线程,这可能很有用。如果要在阅读checkpoint之后恢复主线程的状态,则需要首先捕获它。 checkpoint :: MonadBaseControl b m => m (m ()) checkpoint = liftBaseWith (\runInBase -> runInBase (return ())) >>= return . restoreM 将捕获整个状态并返回一个恢复它的操作。

fork

一个完整的例子将展示两个线程对状态的影响。两个线程都从checkpoint发生时的状态获得状态,而不管修改另一个线程中的状态的努力。当我们在主线程中等待结果时,主线程中的状态被设置为分叉线程中的状态。我们可以通过运行import Control.Monad.State hiding (mapM) example :: (MonadState String m, MonadBase IO m, MonadBaseControl IO m) => m () example = do get >>= liftBase . putStrLn tg <- liftBase TG.new (_, getResult) <- fork tg (get >>= put . ("In Fork:" ++) >> return 7) get >>= put . ("In Main:" ++) revert <- checkpoint result <- getResult (liftBase . print) result get >>= liftBase . putStrLn revert get >>= liftBase . putStrLn main = do runStateT example "Initial" return () 创建的操作来恢复主线程的状态。

Initial
Right 7
In Fork:Initial
In Main:Initial

此输出

{{1}}