Harvard is again teaching OCaml to its first-year students, and Greg Morrissett again this year invited me to give a guest lecture. I gave a version of the Effective ML talk that I gave last year to the same class.

OCaml seems to be getting some real currency as a teaching language in the US. Harvard, Penn and Cornell are all teaching it to their undergraduates as part of the standard curriculum. And SML has of course been taught for a long time at CMU, and Brown and Northeastern teach Scheme/Racket.

As a side note, the class has grown considerably, with over 200 students enrolled in cs51. Apparently, a lot of the growth is the result of people seeing “The Social Network” and getting inspired to go study Computer Science….

When I gave this talk last, there were some requests for the code snippets, so I’ve included the (updated) snippets below. In order to understand the context of these, watch last year’s talk!

Use uniform interfaces

module type Comparable = sig
  type t

  val compare : t -> t -> [ `Lt | `Eq | `Gt ]

  val ( >= ) : t -> t -> bool
  val ( <= ) : t -> t -> bool
  val ( = ) : t -> t -> bool
  val ( > ) : t -> t -> bool
  val ( < ) : t -> t -> bool
  val ( <> ) : t -> t -> bool
  val min : t -> t -> t
  val max : t -> t -> t

  module Map : Core_map.S with type key = t
  module Set : Core_set.S with type elt = t
end
module Char : sig
  type t
  include Comparable with type t := t
  include Stringable with type t := t
  include Hashable with type t := t
end

Make illegal states unrepresentable

Before:

type connection_state =
| Connecting
| Connected
| Disconnected

type connection_info = {
  state: connection_state;
  server: Inet_addr.t;
  last_ping_time: Time.t option;
  last_ping_id: int option;
  session_id: string option;
  when_initiated: Time.t option;
  when_disconnected: Time.t option;
}

After:

type connecting = { when_initiated: Time.t; }
type connected = { last_ping : (Time.t * int) option;
          session_id: string; }
type disconnected = { when_disconnected: Time.t; }

type connection_state =
| Connecting of connecting
| Connected of connected
| Disconnected of disconnected

type connection_info = {
  state : connection_state;
  server: Inet_addr.t;
}

Code for exhaustiveness

type message = | Order of Order.t
               | Cancel of Order_id.t
               | Exec of Execution.t

let position_change m =
  match m with
  | Exec e ->
    let dir = Execution.dir e in
    Dir.sign dir * Execution.quantity e
  | _ -> 0

Open few modules

Before:

open Core.Std
open Command
open Flag

type config = { exit_code: int;
        message: string option; }


let command =
  let default_config = { exit_code = 0; message = None } in
  let flags =
    [ int "-r" (fun cfg v -> { cfg with exit_code = v });
      string "-m" (fun cfg v -> { cfg with message = v });
    ]
  in
  let main cfg =
    Option.iter cfg.message (fun x -> eprintf "%s\n" x);
    cfg.exit_code
  in
  create ~summary:"does nothing, successfully"
    ~default_config ~flags ~main

let () = run command

After:

open Core.Std

type config = { exit_code: int;
        message: string option; }


let command =
  let default_config = { exit_code = 0; message = None } in
  let flags =
    let module F = Command.Flag in
    [ F.int "-r" (fun cfg v -> { cfg with exit_code = v });
    F.string "-m" (fun cfg v -> { cfg with message = v });
    ]
    in
    let main cfg =
      Option.iter cfg.message (fun x -> eprintf "%s\n" x);
      cfg.exit_code
    in
    Command.create ~summary:"does nothing, successfully"
    ~default_config ~flags ~main

let () = Command.run command

Make common errors obvious

module List : sig
  type 'a t

  ....

  val find : 'a t -> ('a -> bool) -> 'a option
  val find_exn : 'a t -> ('a -> bool) -> 'a

  val hd : 'a t -> 'a option
  val hd_exn : 'a t -> 'a

  val reduce : 'a t -> ('a -> 'a -> 'a) -> 'a option
  val reduce_exn : 'a t -> ('a -> 'a -> 'a) -> 'a

  val fold : 'a t -> init : 'b -> ('a -> 'b -> 'b) -> 'b

  .....

end