Монада продолжения и асинхронные ответы

#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 допускает такой монадический поток.

Он просто блокирует процесс, ожидающий следующего запроса. Это можно сделать в обычной монаде ввода-вывода. но необходимы дополнительные эффекты для управления кнопкой возврата и для восстановления состояния выполнения после тайм-аута.