Поднимите, чтобы зафиксировать * внутреннюю * часть стека трансформаторов монад

Предположим, у меня есть 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 - это целая куча функций с отслеживанием состояния, которые не имеют ничего общего с вводом-выводом, поэтому я немного не хочу, чтобы они все возвращали StateT MyState IO a, если я могу этого избежать.


person Adrian May    schedule 29.11.2014    source источник
comment
Вы правы, желая, чтобы типы были точными. Вы можете использовать mapStateT (return . runIdentity) или, в более общем смысле, hoist (return . runIdentity). См. stackoverflow.com/questions/20066858/   -  person danidiaz    schedule 29.11.2014
comment
Спасибо! Это сработало.   -  person Adrian May    schedule 29.11.2014
comment
Обратите внимание, что mmorph теперь имеет функцию generalize: generalize = return . runIdentity.   -  person Gabriel Gonzalez    schedule 03.12.2014


Ответы (1)


У вас есть два варианта:

  1. Найдите морфизм монады. Часто это вопрос поиска подходящей библиотеки; в этом случае подъемник и обобщить вместе должны доставить вас туда, куда вам нужно.
  2. Сделайте ваше 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, поэтому вы получаете те же поведенческие гарантии.

person Daniel Wagner    schedule 29.11.2014
comment
Я прокомментировал это как ответ ниже, чтобы учесть код. - person Adrian May; 01.12.2014
comment
Что касается второго варианта: стоит знать, что разные экземпляры MonadState имеют разные свойства строгости. - person Jeremy List; 20.10.2016