Поднимите, чтобы зафиксировать * внутреннюю часть * монадного стека трансформаторов.
Предположим, у меня есть IO Int
завернутый в StateT MyState
тогда у меня есть значение State MyState Int
который я хочу использовать в сложенной монаде. Как мне поднять это в этом внутреннем смысле? Я уже знаю использовать lift
или же liftIO
если я получаю что-то совместимое с внутренней частью, что мне просто нужно поднять к внешней монаде, но теперь у меня возникает противоположная проблема: значение уже во внешней монаде, но не во внутренней.
Например:
checkSame :: State MyState a -> IO a -> StateT MyState IO Bool
checkSame sim real = do
rres <- liftIO real
sres <- ??? sim
return $ rres == sres
Должен ли я "получить" состояние, протолкнуть его через runState вручную и собрать все заново, или есть какой-то общий способ сделать это?
Кстати, этот параметр sim - это целая куча функций с состоянием, которые не имеют ничего общего с IO, поэтому я не очень-то хочу заставить их все вернуться StateT MyState IO a
если я могу избежать этого.
1 ответ
У вас есть два варианта:
- Найдите морфизм монады. Это часто вопрос поиска правильной библиотеки; в этом случае подъем и обобщение должны привести вас туда, куда вам нужно идти.
Сделай свой
State
действие более полиморфное. Это обычно используемый и рекомендуемый; это равносильно предварительному применению морфизма из части 1, но в нем уже заложено много механизмов.mtl
библиотека, чтобы сделать это легко. Идея в том, что если вы напишитеState
действие только с точки зренияget
,put
, а такжеmodify
тогда вместо типаState s a
Вы можете указать тип:MonadState s m => m a
Затем на сайте вызовов вы можете выбрать любую монаду, подходящую для этого, включая
State s a
а такжеStateT s IO a
, Более того, так как он специализируется на типеState s a
, вы можете быть уверены, что это не делаетIO
или что-нибудь подобноеState s a
Сам не смог бы сделать, поэтому вы получаете такие же поведенческие гарантии.