Выполнить монадический код из вновь созданной монады

В настоящее время у меня есть две монады, которые имеют одинаковые типы, реализованные аналогично State монада:

newtype FooRead  a = FooRead  { runFooRead  :: Context -> (a,Context) }
newtype FooWrite a = FooWrite { runFooWrite :: Context -> (a,Context) }

Разница между ними заключается в том, что первый позволяет только читать контекст (bind не изменит его), а второй позволяет также редактировать контекст.

Затем есть функции, которые используют контекст в FooRead вычислить некоторое значение без изменения состояния контекста:

getVal :: FooRead a
getVal = do x <- ...
            return x

Теперь я хочу выполнить одну из этих функций чтения из кода в монаде писателя:

writeFunc :: FooWrite ()
writeFunc = do x <- liftVal getVal
            ...

куда liftVal :: FooRead a -> FooWrite a это функция, которая извлекает значение, возвращаемое FooRead функция и катит его в FooWrite монада. Это все хорошо.

Тем не менее, я не могу придумать способ свернуть выполнение getValв вышеизложенном в контексте от моего FooWrite монада. С вышеупомянутой реализацией, getVal будет запущен в пустом экземпляре монады.

Я могу понять, как построить FooRead экземпляр, использующий FooWrite контекст как

lower :: FooWrite a -> FooRead a

По сути, я хочу понизить моего писателя до читателя, выполнить код внутри читателя, повторно рекламировать его для писателя.

Но не как на самом деле выполнить код в этой монаде?

1 ответ

Решение

Вот как я бы это реализовал. Во-первых, если вы хотите Writer быть строго более могущественным, чем Reader чем мы хотели бы функцию

liftReader :: FooReader a -> FooWriter a
liftReader (FooReader a) = FooWriter a

Это работает, потому что они структурно эквивалентны и их экземпляры монад должны быть изоморфными.

Тогда мы можем просто

t :: FooWriter Int
t = liftReader getVal

Если вы хотите пойти по другому пути, это довольно легко

liftWriter :: FooWriter a -> FooReader a
liftWriter (FooWriter a) = FooReader a

Теперь тот факт, что мы можем поднять эти типы друг на друга, заставляет вас думать, что они как-то эквивалентны.. И на самом деле они есть, у вас в основном есть

import Control.Monad.State
newtype FooReader s a = FooReader (State s a)
newtype FooWriter s a = FooWriter (State s a)

А также State обеспечивает get а также put которые аналогичны вашим getVal а также writeFunc

Другие вопросы по тегам