Я ищу стандартный способ использования монадных трансформаторов внутри 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
Любые идеи, как сделать то же самое с выходом из библиотеки, приветствуются.