【问题标题】:Does MonadTransControl support STM?MonadTransControl 是否支持 STM?
【发布时间】:2020-05-04 18:34:55
【问题描述】:

我正在寻找一种在 STM.atomically 中使用单子转换器的标准方法。 我认为这听起来很奇怪,因为到目前为止我发现的所有用例都只是 liftIO 。原子地和 通过裸“STM a”而不将其包装到任何单子变压器中。 我想,因为原子动作通常不是很复杂 - 只是 oneliner 和 通过局部变量传递参数更容易、更有效, 但就我而言,交易量很大,我想顺利通过主堆栈。

在熟悉了 monad-control 库之后,我倾向于发表意见 如果源和结果库,runInBase 无法解除 monad 堆栈 monad 是不同的,但我不确定。

inNestedState :: MyData -> StateT MyState STM ()

loadCounterA :: MyData -> StateT MyState IO ()
loadCounterA md = do
   control $ \runInBase -> atomically (runInBase (inNestedState md))

第一个错误

monad-control-demo.hs:29:4: error:
    • Couldn't match type ‘STM’ with ‘IO’
      Expected type: StateT MyState IO ()
        Actual type: StateT MyState STM ()
    • In a stmt of a 'do' block:
        control $ \ runInBase -> atomically (runInBase (inNestedState md))
      In the expression:
        do control
             $ \ runInBase -> atomically (runInBase (inNestedState md))
      In an equation for ‘loadCounterA’:
          loadCounterA md
            = do control
                   $ \ runInBase -> atomically (runInBase (inNestedState md))
   |
29 |    control $ \runInBase -> atomically (runInBase (inNestedState md))

与此同时,我最终得到了有限但方便的自制解决方案:

class MonadRebase m n where
    rebase :: (m a) -> (n a)

instance (MonadRebase m n) => MonadRebase (ReaderT r m) (ReaderT r n) where
    rebase t = ReaderT $ \r -> rebase (runReaderT t r)

instance (MonadRebase m n) => MonadRebase (StateT s m) (StateT s n) where
    rebase t = StateT $ \s -> rebase (runStateT t s)

instance MonadRebase STM IO where
    rebase = atomically

data MyState = MyState {
      msCounter1 :: TVar Int,
      msCounter2 :: TVar Int,
      ops :: Int
    }

inStm :: ReaderT Int (StateT MyState STM) ()
inStm = do
  rInt <- ask
  lift $ do
          mySt <- get
          modify (\st -> st {ops = rInt + ops st })
          lift $ do
            c1Val <- (readTVar (msCounter1 mySt))
            c2Val <- (readTVar (msCounter2 mySt))
            writeTVar (msCounter1 mySt) 0
            writeTVar (msCounter2 mySt) (c1Val + c2Val)

foo :: ReaderT Int (StateT MyState IO) ()
foo = do
  rebase inStm

感谢任何想法如何对现有库执行相同操作。

【问题讨论】:

    标签: haskell monads monad-transformers


    【解决方案1】:

    我将您的问题解释为“如何将 StateT MyState STM () 转换为 StateT MyState IO ()?”。答案是mapStateT

    loadCounterA = mapStateT atomically . inNestedState
    

    如您的第二个示例中那样,要深入转换器堆栈的多个层,只需嵌套转换器各自 map 函数的应用程序:

    foo = mapReaderT (mapStateT atomically) inStm
    

    当你有一个大的转换器堆栈时,这可能有点繁琐,但由于类型检查器,这是你不会出错的那种代码。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-08-08
      • 1970-01-01
      • 1970-01-01
      • 2021-06-07
      • 2020-04-23
      • 2019-08-28
      • 2015-07-04
      相关资源
      最近更新 更多