#haskell #asynchronous #continuations
#haskell #асинхронный #продолжения
Вопрос:
Предположим, что у нас есть три синхронные функции, которые получают данные по 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) мог бы сослаться на реальный пример того, как кто-то это делает, я был бы благодарен.
Ответ №1:
Продолжения — это просто шаблон работы с функциями, они не имеют ничего общего с асинхронностью.
Что вам нужно forkIO
, так это абстракции более высокого уровня, представленные библиотеками, такими как async.
Ответ №2:
Вы можете написать transform
еще более обобщенно, чем это:
transform :: Monad m => (a -> m b) -> a -> ContT r m b
transform k = lift . k
это работает для любой монады, а не только для ввода-вывода. Но это не то, чего вы действительно хотите. Как упоминает Никита Волков, продолжения не являются решением, если вы хотите реального асинхронного программирования.
Я бы также рекомендовал библиотеку async.
Ответ №3:
Веб-платформа MFlow допускает такой монадический поток.
Он просто блокирует процесс, ожидающий следующего запроса. Это можно сделать в обычной монаде ввода-вывода. но необходимы дополнительные эффекты для управления кнопкой возврата и для восстановления состояния выполнения после тайм-аута.