(* Now that we've written a hello world program, let's dive into some more
* meaty async code. Hopefully, most of this should be familiar to you after
* reading Chapter 18 of Real World OCaml. *)
open Core.Std
open Async.Std
(* First up, let's play with deferreds and bind. An 'a Deferred.t (or
* colloquially, an 'a deferred) represents a value of type 'a that may or may
* not yet be determined. For example, consider calling a function
* read_line_from_stdin that reads a line from the user and returns the line as
* a string deferred. Now imagine you call read_line_from_stdin and get some
* string deferred s. s may or may not yet be determined. If the user hasn't
* typed in anything yet, then s hasn't yet been determined. Once the user
* types something in, s becomes determined with whatever the user typed.
*
* I like to think of deferreds as cardboard boxes that may or may not contain
* a value. When a deferred isn't yet determined, I think of it as an empty
* box. When a deferred is determined, I imagine the box is full with some
* value.
*
* So if you give me a deferred, and it's not yet determined, what do I do with
* it? Well, if the cardboard box is full, we could open it up, pull out the
* value and do whatever we want with it. But if the cardboard box is empty,
* we're in a bit of a pickle. We could sit and wait around twiddling our
* thumbs until the box is full and then unpack it, but that wouldn't be
* asynchronous at all (which, for a library named async, would be pretty
* unfortunate).
*
* Enter bind.
*
* Bind is a higher order function that is fundamental to programming with
* deferreds. Here's the type of bind:
*
* 'a Deferred.t -> ('a -> 'b Deferred.t) -> b' Deferred.t
*
* Let's call the first argument x and the second f. Bind registers f to be
* called on the value of x once it becomes determined. In other words, bind
* will wait until our cardboard boxes are full before passing their contents
* to a provided function. We can chain together a sequence of binds to create
* complex computations, as shown in the code below. *)
(* This function calls bind with two arguments. The first is (return 1), which
* is an int deferred. The second is the very big function (fun x -> ... return
* ()))), which is a function from int deferred to unit deferred. When (return
* 1) becomes determined, the 1 is unwrapped and the very big function is
* called with x bound to 1. This very big function calls bind again with two
* arguments. The first is return 2 and the second is another fairly big
* function (fun y -> ... return ())). Again, when (return 2) becomes
* determined, the 2 is unwrapped and passed into the fairly big function such
* that y is bound to 2. This pattern continues a third time after which z is
* bound to 3. And finally, we run the code which prints the sum of x, y, and z
* and then return ().
*
* Phew! For summing up three numbers, that's quite a bit of code to think
* about. Well, while it's nice to know that in reality bind is scheduling
* functions to run when deferreds become determined, we can alternatively
* ignore all this and consider a simpler way of thinking about bind and
* deferreds. Whenever you see code of the form `Deferred.bind d (fun x -> e)`,
* you can think about the code as the following: `let x = (unwrap d) in e`.
* That is, whenever you see a deferred being bound to a function, just imagine
* instead you unwrap the value of the deferred and bind it to argument of
* function directly using a let expression. If we think of async code like
* this, then the following function would look something like this:
*
* let x = 1 in
* let y = 2 in
* let z = 3 in
* printf "1 + 2 + 3 = %d\n" (x + y + z);
*
* This code is pretty simple. A key to grokking with async code is knowing
* when to think of binds as scheduling functions to be run deferreds become
* determined, and when to ignore all that and just think of binds as let
* expressions.
*)
let sum123 () : unit Deferred.t =
Deferred.bind (return 1) (fun x ->
Deferred.bind (return 2) (fun y ->
Deferred.bind (return 3) (fun z ->
printf "1 + 2 + 3 = %d\n" (x + y + z);
return ()
)))
(* There's also an infix operator >>= that works exactly the same way as
* Deferred.bind. The following function works exactly the same as the previous
* one. *)
let sum456 () : unit Deferred.t =
return 4 >>= (fun x ->
return 5 >>= (fun y ->
return 6 >>= (fun z ->
printf "4 + 5 + 6 = %d\n" (x + y + z);
return ())))
(* The benefit of using >>= over Deferred.bind is that we can remove a lot of
* the parentheses we needed before! This function does exactly the same thing
* that the previous two functions did! *)
let sum789 () : unit Deferred.t =
return 7 >>= fun x ->
return 8 >>= fun y ->
return 9 >>= fun z ->
printf "7 + 8 + 9 = %d\n" (x + y + z);
return ()
(* Recall that the type of bind is 'a Deferred.t -> ('a -> 'b Deferred.t) -> 'b
* Deferred.t. In the previous three functions, 'a was instantiated with int,
* 'b was instantiated with unit. Thus, the functions were binding into had to
* return a unit deferred. This is why we ended the functions with return ().
* We can save some keystrokes by instead using >>| which has the type 'a
* Deferred.t ('a -> 'b) -> 'b Deferred.t. >>| (pronounced map) acts pretty
* much the same as bind except the function you map into returns a normal 'b
* instead of a 'b deferred. You could easily implement >>| using >>= like this:
*
* let (>>|) deferred f =
* deferred >>= (fun x -> return (f x))
*
* Since printf returns a unit, we can change our last bind to a map and avoid
* having to return (). *)
let sum111 () : unit Deferred.t =
return 1 >>= fun x ->
return 1 >>= fun y ->
return 1 >>| fun z ->
printf "1 + 1 + 1 = %d\n" (x + y + z)
(* However, we can't always swap out >>= for >>|. For example, uncomment the
* following code and try to build this program. You'll get an error that this
* function returns something of type unit Deferred.t Deferred.t Deferred.t but
* was expecting something of type unit Deferred.t! What happened? Well
* remember that >>| takes in a function from 'a to 'b, and returns the output
* to make a 'b deferred. printf returns a () and since we have three
* occurrences of >>|, each one adds a layer of deferred to make a unit
* Deferred.t Deferred.t Deferred.t!
*
* In general, avoid chaining together lambdas with >>| and instead stick with
* >>=. *)
let sum222 () : unit Deferred.t =
(*
return 2 >>| fun x ->
return 2 >>| fun y ->
return 2 >>| fun z ->
printf "2 + 2 + 2 = %d\n" (x + y + z)
*)
return ()
let main () : unit Deferred.t =
sum123 () >>= fun () ->
sum456 () >>= fun () ->
sum789 () >>= fun () ->
sum111 () >>= fun () ->
sum222 () >>= fun () ->
return ()
let () =
Command.(run (async ~summary:"basic async" Spec.empty main))