Почему вам не нужно использовать 'lift' при взаимодействии с вложенным Монадтом StateT в этом случае?

скажем, у меня есть monadT:

type Wrap a = ReaderT Env ( StateT Int ( StateT Int Identity ) ) a

здесь важно отметить, что один StateT обертывает другой, и оба завернуты внутри третьей монады, а именно ReaderT.

и соответствующая функция runWrap для удобства:

type Env = Map.Map Char Integer

runWrap :: Env -> Int -> Int -> Wrap a -> a
runWrap env st1 st2 m = runIdentity $ evalStateT ( evalStateT ( runReaderT m env ) st2 ) st1

и общая монада состояния тока:

tock :: (Num s, MonadState s m) => m ()
tock = do modify (+1)

я теперь с запахом monadT, где внутри я использую так:

aWrap :: Wrap ( Int, Int )
aWrap = do
    lift tock
    lift . lift $ tock
    x    <- get
    y    <- lift . lift $ get
    return ( x, y )

и запустить его:

env = Map.fromList [('x', 1)]
runWrap env 1 200 aWrap
// answer: (201,2)

использование из lift здесь имеет смысл для меня с точки зрения моего понимания того, как взаимодействовать с вложенными слоями Монадта.

однако это также работает и дает мне тот же ответ:(201,2):

aWrap :: Wrap ( Int, Int )
aWrap = do
    tock
    lift . lift $ tock
    x    <- get
    y    <- lift . lift $ get
    return ( x, y )

я бы подумал, позвонив tock Вт/lift, он читается как tock применяется для наружных MonadT, а именно ReaderT, что не имеет никакого смысла. Но почему это работает?

P. S. пожалуйста, игнорировать присутствие Env здесь, это не имеет ничего общего с вопросом, просто выбор внешнего MonadT я использую.

1 ответов


скорее всего, вы используете MonadState typeclass, не осознавая этого. Этот typeclass определена в (и monads-fd тоже).

MonadState позволяет использовать методы State монада, непосредственно и без явного подъема, во многих стеках монады на основе State.

посмотрите на следующие две строки в рода хаддоков:

Monad m => MonadState s (StateT s m)
MonadState s m => MonadState s (ReaderT r m)

первый говорит, что любой StateT пример MonadState (как и следовало ожидать!). Второй говорит, что любой ReaderT чья базовая монада является instace MonadState, также является экземпляром MonadState. И это как раз твое дело.

смотреть на!--28-->исходный код на MonadState, мы находим:

instance MonadState s m => MonadState s (ReaderT r m) where
    get = lift get
    put = lift . put
    state = lift . state

modify :: MonadState s m => (s -> s) -> m ()
modify f = state (\s -> ((), f s))

как вы видите, внутренний механизм typeclass заботится о подъеме.

есть другие typeclasses, которые предлагают аналогичную функциональность, как MonadReader, MonadWriter и MonadRWS.