let main () : unit Deferred.t =
  (* First up, we create a Reader.t from stdin. Luckily, the Reader module has
   * exactly this already! The only hiccup is that Reader.stdin is of type
   * `Reader.t Core.Std.Lazy.t`. It's not too important what that means; all we
   * have to do is call Lazy.force to get the Reader.t we're looking for. *)

  let stdin = Lazy.force Reader.stdin in

  (* The Reader module has a lot of functions to read from a Reader.t:
   * Reader.read, Reader.read_char, Reader.really_read, etc. For now, we just
   * want to read a single from the user, so we'll use Reader.read_line. As
   * the name suggests, it reads all the data up to and including the newline
   * character.
   *
   * Of course, reading could go wrong. For example, we could read from a file
   * that's been deleted or read from a TCP connection that's been closed. So,
   * Reader.read_line, and all the other reading functions in the Reader
   * module, return a Read_result.t. A Read_result.t is either `Eof (signifying
   * we've hit the end of the reader) or an `Ok x, where the x is the data
   * we're after. If our read here doesn't work, we'll just print "error".
   * Otherwise, we'll print whatever line the user typed in. *)

  Reader.read_line stdin >>| function
  | `Eof  -> print_endline "error"
  | `Ok x -> print_endline x