В вашем коде нет, потому что вы используете Lwt.t
как монаду, а не как аппликатив.
Монады
Возможно, вы уже знакомы с асинхронным вводом-выводом и функциямиLwt.bind : 'a Lwt.t -> ('a -> 'b Lwt.t) -> 'b Lwt.t
и Lwt.return : 'a -> 'a Lwt.t
.Однако на всякий случай я приведу краткое резюме:
Lwt.bind promise callback
ожидает promise
, а после разрешения вызывает callback
с результатом, получая еще одно обещание. Lwt.return data
создает обещание, которое разрешается до data
.
Монада - это универсальный тип 'a t
, который имеет некоторую функцию bind : 'a t -> ('a -> 'b t) -> 'b t
и некоторую функцию return : 'a -> 'a t
.(Эти функции также должны следовать определенным законам, но я отвлекся.) Очевидно, что тип 'a Lwt.t
с функциями Lwt.bind
и Lwt.return
образует монаду.
Монады - это общий шаблон функционального программирования, когда одинхочет представить какой-то «эффект» или «вычисление», в данном случае асинхронный ввод-вывод.Монады являются мощными, потому что функция bind
позволяет последующим вычислениям зависеть от результатов предыдущих.Если m : 'a t
представляет какое-то вычисление, которое приводит к 'a
, а f : 'a -> 'b t
является функцией, которая использует 'a
для выполнения вычисления, которое приводит к 'b
, то bind m f
делает f
зависимым отрезультат m
.
В случае Lwt.bind promise callback
, callback
зависит от результата promise
.Код в callback
не может быть запущен, пока не будет разрешен promise
.
Когда вы пишете
let%lwt x = f () in
let%lwt y = g () in
return ()
, вы действительно пишете Lwt.bind (f ()) (fun x -> Lwt.bind (g ()) (fun y -> return ()))
.Поскольку g ()
находится внутри обратного вызова, он не запускается до тех пор, пока не будет разрешен f ()
.
Применимые
Функциональный шаблон программирования, связанный с монадой, является аппликативным.Аппликатив - это универсальный тип 'a t
с функцией map : ('a -> 'b) -> 'a t -> 'b t
, функцией return : 'a -> 'a t
и функцией both : 'a t * 'b t -> ('a * 'b) t
.Однако, в отличие от монад, аппликативам не нужно иметь bind : 'a t -> ('a -> 'b t) -> 'b t
, что означает, что с одними аппликативами более поздние вычисления не могут зависеть от предыдущих.Все монады являются аппликативными, но не все аппликативные являются монадами.
Поскольку g ()
не зависит от результата f ()
, ваш код можно переписать для использования both
:
let (let*) = bind
let (and*) = both
let* x = f ()
and* y = g () in
return ()
Этот код переводится как bind (fun (x, y) -> return ()) (both (f ()) (g ()))
.f ()
и g ()
появляются за пределами обратного вызова к bind
, что означает, что они запускаются немедленно и могут ожидать в параллельном режиме.both
объединяет f ()
и g ()
в одно обещание.
Операторы (let*)
и (and*)
являются новыми для OCaml 4.08.Если вы используете более раннюю версию OCaml, вы можете просто написать перевод напрямую.