Если вы хотите использовать базовую монаду в преобразователе монад, вы можете использовать lift
:
lift :: (MonadTrans t, Monad m) => m a -> t m a
В этом случае t
равно StateT MyState
, а m
равно MyMonad
. Так, например:
foo :: StateT MyState MyMonad MyType
foo = do
modify $ \s -> s+1
lift $ doSomethingInMyMonad 42
Преобразователи монады не «наслоены» в том смысле, что вы возвращаете значение типа MyMonad MyType
изнутри; это более буквальное преобразование : они превращают монаду в новую, способную выполнять действия в преобразованной монаде. Таким образом, вы можете думать о StateT s m
как о обычной монаде State s
, за исключением того, что вы также можете использовать lift
для запуска превращения действий в m
в действия в StateT s m
.
Если вы используете стандартные Monad Transformer Library (mtl) преобразователи, такие как StateT
, ReaderT
и т. Д., Вам на самом деле не нужно использовать lift
; такие вещи, как modify
и ask
работают в любой монаде с правильным преобразователем где-то в стеке. (Стек - это просто башня трансформированных монад, типа StateT s (ReaderT r IO)
.)
Кроме того, если у вас большой стек с IO
внизу, есть удобная функция для подъема действия IO
на любое количество слоев:
liftIO :: (MonadIO m) => IO a -> m a
Итак liftIO (putStrLn "Hello, world!")
работает в IO
, StateT Int IO
, ContT r (WriterT [String] IO)
и т. Д.
(Как дополнительное примечание, foo
здесь на самом деле не является функцией; более точный термин - action или вычисление .)