Это может быть хорошей идеей для рефакторинга вашего кода, чтобы использовать тип StateT SomeType m a
вместо State SomeType a
, потому что первый совместим с произвольным стеком монад.Если вы измените его следующим образом, вам больше не нужна функция wrapST
, поскольку вы можете напрямую вызывать функции с состоянием.
Хорошо.Предположим, у вас есть функция subOne :: Monad m => State Int Int
:
subOne = do a <- get
put $ a - 1
return a
Теперь измените типы всех функций, подобных этой, с State SomeType a
на StateT SomeType m a
, оставив m
как есть.Таким образом, ваши функции могут работать с любым монадическим стеком.Для тех функций, которые требуют ввода / вывода, вы можете указать, что монада внизу должна быть IO:
printState :: MonadIO m => StateT Int m ()
printState = do a <- get
liftIO $ print a
Теперь должна быть возможность использовать обе функции вместе:
-- You could use me without IO as well!
subOne :: Monad m => StateT Int m ()
subOne = do a <- get
put $ a - 1
printState :: MonadIO m => StateT Int m ()
printState = do a <- get
liftIO $ print a
toZero :: StateT Int IO ()
toZero = do subOne -- A really pure function
printState -- function may perform IO
a <- get
when (a > 0) toZero
PS: я использую GHC 7, некоторые библиотеки изменились на полпути, поэтому на GHC 6 это может немного отличаться.