Commits

datkin  committed 24b06ba

Core.Std notes, examples and exercises for ocaml workshop.

  • Participants
  • Parent commits 31415b8

Comments (0)

Files changed (7)

File workshop/count-lines-1/main.ml

+open Core.Std
+
+let rec build_counts counts =
+  match In_channel.input_line stdin with
+  | None -> counts (* EOF *)
+  | Some line ->
+    let count =
+      match List.Assoc.find counts line with
+      | None -> 0
+      | Some x -> x
+    in
+    build_counts (List.Assoc.add counts line (count + 1))
+
+let count_lines () =
+  let counts = build_counts [] in
+  let sorted_counts = List.sort ~cmp:(fun (_, x) (_, y) -> Int.descending x y) counts in
+  List.iter (List.take sorted_counts 10) ~f:(fun (line, count) ->
+    printf "%3d: %s\n" count line)
+
+let count_lines_cmd =
+  Command.basic
+    ~summary:"Count top 10 unique lines in a file"
+    Command.Spec.(
+      empty
+    )
+    count_lines
+;;
+
+let command =
+  Command.group
+    ~summary:"A bunch of counting tools"
+    [ "count-lines", count_lines_cmd
+    ]
+
+let () = Command.run command

File workshop/count-lines-2/counter.ml

+open Core.Std
+
+type t =
+  (* equivalent to [(string * int) list] *)
+  (string, int) List.Assoc.t 
+with sexp
+
+let empty = []
+
+let touch t str =
+  let count =
+    match List.Assoc.find t str with
+    | None -> 0
+    | Some x -> x
+  in
+  List.Assoc.add t str (count + 1)
+
+let top_n t n =
+  let sorted_counts = List.sort ~cmp:(fun (_, x) (_, y) -> Int.descending x y) t in
+  List.take sorted_counts n

File workshop/count-lines-2/counter.mli

+open Core.Std
+
+(* Exercises:
+
+   - Change the underlying representation to use [String.Map.t]
+
+   - Add two functions
+
+       val median_element : t -> string
+       val median_occurrences : t -> int
+
+   - Technically, if there are an even number of unique elements,
+     there is no single median element, but rather one element before
+     the median and one element after. Change the median function to
+     reflect this.
+
+   - Make the median functions O(1).
+*)
+
+type t with sexp
+
+val empty : t
+
+val touch : t -> string -> t
+
+val top_n : t -> int -> (string * int) list

File workshop/count-lines-2/main.ml

+open Core.Std
+
+let rec build_counts counter =
+  match In_channel.input_line stdin with
+  | None -> counter (* EOF *)
+  | Some line -> build_counts (Counter.touch counter line)
+
+let count_lines () =
+  let counter = build_counts Counter.empty in
+  List.iter (Counter.top_n counter 10) ~f:(fun (line, count) ->
+    printf "%3d: %s\n" count line)
+
+let count_lines_cmd =
+  Command.basic
+    ~summary:"Count top 10 unique lines in a file"
+    Command.Spec.(
+      empty
+    )
+    count_lines
+
+let command =
+  Command.group
+    ~summary:"A bunch of counting tools"
+    [ "count-lines", count_lines_cmd
+    ]
+
+let () = Command.run command

File workshop/hello-world/main.ml

+open Core.Std
+
+(* Exercises:
+
+   - Add a subcommand for writing new greeting files, with this interface:
+
+     $ ./main.byte make-greeting -name Joe -greeting Yo greet-joe.sexp
+*)
+
+module Message : sig
+  type t with sexp
+
+  val to_string : t -> string
+end = struct
+  type greeting =
+  | Hello
+  | Goodbye
+  with sexp
+
+  type t =
+    { name : string
+    ; greeting : greeting
+    } with sexp
+      
+  let to_string t =
+    sprintf "%s, %s"
+      (Sexp.to_string (sexp_of_greeting t.greeting))
+      t.name
+end
+
+let greet_cmd =
+  Command.basic
+    ~summary:"Greet someone from a message file"
+    Command.Spec.(
+      empty
+      +> anon ("message-file" %: file) 
+    )
+    (fun file () ->
+      (* Read an s-expression from the given file, and uses
+         [Messgae.t_of_sexp] to convert the s-expression to a
+         [Message.t] *)
+      let message = Sexp.load_sexp_conv_exn file Message.t_of_sexp in
+      printf "%s\n%!" (Message.to_string message))
+
+let command =
+  Command.basic
+    ~summary:"Greeting commands"
+    [ "greet", greet_cmd
+    ]
+
+let () =
+  (* Wrapping [Command.run] in [Exn.handle_uncaught] is necessary for
+     better error messages from the sexplib when a [t_of_sexp]
+     function fails. *)
+  Exn.handle_uncaught ~exit:true (fun () -> Command.run command)

File workshop/notes.org

+* Answers to questions
+** When to use ocamlbuild vs ocaml{c,opt} and .byte vs .opt
+ - ocamlc is the byte-code compiler
+   supports more platforms, ocaml debugger support, probably compiles faster?
+ - ocamlopt is the native-code compiler
+   faster code!
+ - ocamlbuild is just a tool to manage more complex builds (may
+   invoke ocamlc* multiple times under the hood)
+ - both ocamlc and ocamlopt are byte-code programs, but there are
+   native-compiled versions (ending with ".opt").
+** Difference between [open] and [include]
+open_vs_include.ml
+** Other questions?
+* Intro to Core
+** Provides lots of useful modules
+*** Data structures
+ - list
+ - map
+ - set
+ - hash table
+ - hash set
+ - doubly-linked list
+ - heap
+ - avl tree
+ - queue
+ - bag
+ - stack
+ - dequeue
+ - univ_map
+*** Time modules
+ - time
+ - timezone
+ - date
+ - weekday
+ - ofday
+ - time
+*** Error handling
+ - option
+ - result
+ - error
+ - info
+ - or_error
+ - exn
+ - with_return
+*** "Pratical stuff"
+ - unix
+ - {in,out}_channel
+ - command
+ - blang
+ - sexp
+ - bin_io
+*** Interfaces
+ - container
+ - comparable
+ - hashable
+ - stringable
+ - monad
+** Very careful about following conventions
+ - explicit error handling (different from the stdlib)
+   eg [List.hd : 'a list -> 'a] vs [Core.List.hd : 'a list -> 'a option]
+ - redefines many modules in the stdlib, with incompatible interfaces
+ - the [type t] convetion
+   each module usually defines a datatype, and operations on that
+   datatype -- within that module, the type will be called simply
+   [t].
+ - see http://janestreet.github.com/coding-standards.html
+** Understanding the .mli's
+ - best way to learn an ocaml library (if it's well written, the
+   types should tell the story)
+ - understanding interface includes (see, eg, the Map interface)
+ - opam makes mli's available, eg: ~/.opam/system/lib/core/*
+* "Hello World" example 
+** Command line parsing
+ - ../hello_world.ml
+ - [open Core.Std] at the top
+ - [let () = Command.run]
+ - [Command.Spec.( ... )], [(+>)] is just an infix function
+ - a simple ocaml command-line program with the Command module
+   ../core-hello-world/hello_world.ml
+ - example: add anonymous arg "Hello World, this is _"
+ - exercise: add an optional argument to end the greeting with a "!"
+** File IO, reading s-expressions
+ - hello-world/main.ml
+ - let's make the greeting configurable
+* "Line counts" example -- file io
+ - count-lines-1/main.ml
+ - exercise: add an argument to read input from file(s) or stdin
+* "Line counts" example -- refactoring into modules
+ - count-lines-2/main.ml

File workshop/open_vs_include.ml

+module X = struct
+  let n = 5
+  let add1 x = x + 1
+end
+
+module Y : sig
+  val add2 : int -> int
+end = struct
+  open X
+
+  let add2 x = add1 (add1 x)
+end
+
+module Z : sig
+  val add1 : int -> int
+  val add2 : int -> int
+end = struct
+  include X
+
+  let add2 x = add1 (add1 x)
+end