Выполнить монадический код из вновь созданной монады
В настоящее время у меня есть две монады, которые имеют одинаковые типы, реализованные аналогично 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