Продолжение монады и асинхронных ответов
Предположим, что у нас есть три синхронные функции, которые получают данные через HTTP (это могут быть внутренние вызовы API):
lookupUser :: String -> IO UserId
lookupUserCity :: UserId -> IO City
lookupLocation :: City -> IO Location
Итак, я мог бы просто сделать монадическую композицию lookupLocation <=< lookupUserCity <=< lookupUser
чтобы получить местоположение пользователя. Но, поскольку каждый из этих вызовов будет блокироваться, это предотвратит запуск остальной части программы.
Очевидно, продолжения помогают решить эту проблему, но я не могу найти реальных примеров этого. Я думаю, что подписи будут переписаны a -> ContT r IO b
, но я не вижу, как вы могли бы получить шаблон, похожий на обратный вызов с этим. Если кто-нибудь может показать мне (1), как написать transform :: (a -> IO b) -> (a -> ContT r IO b)
или (2) может быть ссылка на реальный пример того, кто это делает, я был бы благодарен.
3 ответа
Продолжения - это просто шаблон работы с функциями, они не имеют ничего общего с асинхронностью.
Что вам нужно forkIO
или более высокоуровневые абстракции, представленные такими библиотеками, как async.
Ты можешь написать transform
в более общем смысле, то:
transform :: Monad m => (a -> m b) -> a -> ContT r m b
transform k = lift . k
это работает для любой монады, а не только для ввода-вывода. Но это не то, что вы действительно хотите. Как отмечает Никита Волков, продолжения не являются решением, если вам нужно настоящее асинхронное программирование.
Я бы порекомендовал также асинхронную библиотеку.
Веб-фреймворк MFlow разрешает такой монадический поток.
Это просто блокирует процесс, ожидающий следующего запроса. Это можно сделать в обычной монаде IO. но необходимы дополнительные эффекты для управления кнопкой возврата и восстановления состояния выполнения после истечения времени ожидания.