MonadTransControl поддерживает STM? - PullRequest
1 голос
/ 04 мая 2020

Я ищу стандартный способ использования монадных трансформаторов внутри STM.atomically. Я думаю, это звучит странно, потому что все варианты использования, которые я нашел до сих пор, просто liftIO. атомарно и передать «STM a», не оборачивая его в какой-либо монадный трансформатор. Я думаю, потому что действие atomi c обычно не очень сложное - просто однострочный, и проще и эффективнее передавать аргументы через локальные переменные, но в моем случае транзакция велика, и я хотел бы плавно передать основной стек.

После ознакомления с библиотекой управления монадой я склоняюсь к мнению, что runInBase не может отменить стек монад, если исходные и результирующие базовые монады отличаются, но я не уверен.

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

Любые идеи, как сделать то же самое с выходом из библиотеки, приветствуются.

1 Ответ

1 голос
/ 06 мая 2020

Я интерпретирую ваш вопрос как "Как я могу преобразовать StateT MyState STM () в StateT MyState IO ()?". Ответ: mapStateT:

loadCounterA = mapStateT atomically . inNestedState

До go вниз по нескольким слоям комплекта трансформаторов, как во втором примере, просто вложите приложения соответствующих трансформаторов map functions:

foo = mapReaderT (mapStateT atomically) inStm

Когда у вас большой стек преобразователей, это может быть немного неудобно, но это тот код, который вы не ошибетесь благодаря проверке типов.

...