Продолжение монады и асинхронных ответов

Предположим, что у нас есть три синхронные функции, которые получают данные через 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. но необходимы дополнительные эффекты для управления кнопкой возврата и восстановления состояния выполнения после истечения времени ожидания.

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