Запутанное название для запутанного вопроса! Я понимаю: а) монады, б) монада IO, в) монада Конда (Control.Monad.Cont), и г) монада-трансформатора продолжения ContT. (И я смутно понимаю трансформаторы монады в целом - хотя и не достаточно, чтобы ответить на этот вопрос.) Я понимаю, как написать программу, где все функции находятся в Cont monad (Cont r a
), и я понимаю, как написать программу где все функции находятся в комбинированной Монад/Мона (ContT r IO a
).Эвакуация из монады IO внутри монады Продолжение
Но мне интересно, как я мог бы написать программу, где некоторые функции в сочетании прод/IO монаде (ContT r IO a
) и других функций только в монаде Cont (Cont r a
). В принципе, я хочу написать всю программу в стиле продолжения, но только там, где это необходимо, используйте монаду IO (как и в «обычном» коде Haskell, я использую только монашку IO, где это необходимо).
Для примера рассмотрим эти две функции в стиле без продолжения:
foo :: Int -> IO Int
foo n = do
let x = n + 1
print x
return $ bar x
bar :: Int -> Int
bar m = m * 2
Обратите внимание, что foo
требует ввода-вывода, но bar
чист. Теперь я понял, как писать этот код полностью используя продолжение монады, но мне нужно, чтобы нить IO через bar
, а также:
foo :: Int -> ContT r IO Int
foo n = do
let x = n + 1
liftIO $ print x
bar x
bar :: Int -> ContT r IO Int
bar m = return $ m * 2
Я делать хочу весь мой код в стиле продолжения, но я Дон» t хотят использовать монаду IO для функций, которые этого не требуют. В принципе, я хотел бы определить bar
так:
bar :: Int -> Cont r Int
bar m = return $ m * 2
К сожалению, я не могу найти способ, чтобы вызвать функцию Cont r a
монады (bar
) из внутри функции ContT r IO a
монады (foo
). Есть ли способ «поднять» необработанную монаду в преобразованную? то есть, как я могу изменить строку «bar x
» в foo
, чтобы он мог правильно позвонить bar :: Int -> Cont r Int
?
Спасибо. Это работает. Я также нашел свое собственное решение, которое дало мне именно то, что я хотел (мне не пришлось менять «Bar»): 'liftCont :: Cont (m r) a -> ContT r m a'; 'liftCont c = ContT $ runCont c'. Мое решение распаковывает 'Cont' и создает' ContT'. Я думаю, что ваше решение лучше, потому что оно полиморфно и не требует фактической манипуляции с структурами данных, поэтому тикайте для вас. Но я напишу как другой ответ, так как он полезен, если вы не можете изменить 'bar'. Также +1 для объяснения того, почему невозможно было использовать IO в 'bar'. – mgiuca