Как разделить состояние между двумя монадами?

Я застрял со следующей проблемой монады:

Допустим, у меня есть стандартная монада State с состоянием S = (LS, RS). У меня также есть другая монада:

newtype StateP a = StateP {runP :: S -> (a, RS)}

Я хочу выполнить некоторые вычисления, используя StateP, а затем объединить состояние с состоянием в монаде State:

merge m :: StateP() -> State()
merge m = do
 s@(l,r) <- get
 put (l, snd (runP m s))

Он не работает, но я не понимаю, почему? Есть ли другой способ добиться такой функциональности?


person user1030210    schedule 04.11.2011    source источник
comment
В каком смысле не работает? Вы получаете ошибки компиляции (что это такое?) или происходит сбой во время выполнения? Код merge, который вы разместили, выглядит так, как будто он не должен компилироваться --- это ваш настоящий код?   -  person dave4420    schedule 04.11.2011


Ответы (3)


Вы можете использовать преобразователи монад для более явного моделирования этих требований с помощью двух стеков монад: одного, который может только читать LS, и другого, который может читать и записывать LS.

type ReadOnlyLS a  = ReaderT LS (State RS) a
type ReadWriteLS a = StateT LS (State RS) a

Чтобы запустить ReadOnlyLS внутри ReadWriteLS, нам просто нужно извлечь LS из самого внешнего слоя состояния, передать его слою чтения внутреннего вычисления и поднять полученное вычисление обратно во внешнюю монаду:

merge :: ReadOnlyLS a -> ReadWriteLS a
merge m = get >>= lift . runReaderT m
person hammar    schedule 04.11.2011

На случай, если ваш код почти правильный, попробуйте

merge :: StateP() -> State()
merge m = do
    s@(l,r) <- get
    put (l, snd (runP m s))

Но вы должны дать нам больше деталей на самом деле.

person dave4420    schedule 04.11.2011
comment
Привет. Это действительно работает. Я не могу сказать, почему раньше у меня возникали ошибки типов. Спасибо за ответ. - person user1030210; 04.11.2011
comment
О, похоже, у тебя не было do. - person nponeccop; 04.11.2011
comment
это не пропало. я думаю, что мог бы написать fst вместо snd. в любом случае он должен быть помечен для удаления. - person user1030210; 06.11.2011

Как вы достигаете функциональности RunP? Вы переопределяете для него экземпляр монады и имеете getP/putP? Ваш код выглядит нормально, можете ли вы предоставить m, который вы используете? Какое неправильное поведение вы получаете?

person nponeccop    schedule 04.11.2011
comment
Вероятно, это должен быть комментарий, а не ответ. - person Daniel Wagner; 05.11.2011
comment
Что ж, это была заглушка для ответа - я планировал отредактировать ее, как только автор добавит больше деталей. - person nponeccop; 05.11.2011