Notes from Real World OCaml, chapter 7, part 1
This post is part 4 of the “Real World OCaml” series:
- Notes from Real World OCaml, first edition
- Notes from Real World OCaml, chapter 3
- Notes from Real World OCaml, chapters 4 and 5
- Notes from Real World OCaml, chapter 7, part 1
- Notes from Real World OCaml, chapter 7, part 2
In compute_bounds
, cmp
should be replaced with compare
:
let compute_bounds ~compare list =
let sorted = List.sort ~compare list in
match List.hd sorted, List.last sorted with
| None, _ | _, None -> None
| Some x, Some y -> Some (x, y)
The resulting type signature will be the same as in the book.
Typing find_mismatches
into utop
gave a very odd type signature after spinning for a while.
val find_mismatches :
('a, int) Timezone.Table.hashtbl ->
('a, int) Timezone.Table.hashtbl -> 'a list = <fun>
Here’s the function rewritten not to depend on Core:
let find_mismatches table1 table2 =
let f key data mismatches =
match Hashtbl.find_opt table2 key with
| Some data' when data' <> data -> key :: mismatches
| _ -> mismatches
in Hashtbl.fold f table1 [];;
Which gives a more resaonable signature:
val find_mismatches :
('a, 'b) Hashtbl.t -> ('a, 'b) Hashtbl.t -> 'a list = <fun>
On the off-chance that the issue was down to the contents of my ~/.opam
directory, nuked it and reinstalled it:
$ rm -rf ~/.opam
$ opam init
$ opam install core utop
This gave a better result:
utop # open Core;;
utop # let find_mismatches table1 table2 =
Hashtbl.fold table1 ~init:[] ~f:(fun ~key ~data mismatches ->
match Hashtbl.find table2 key with
| Some data' when data' <> data -> key :: mismatches
| _ -> mismatches
);;
val find_mismatches :
('a, int) Core.Hashtbl.t -> ('a, int) Core.Hashtbl.t -> 'a list = <fun>
Notice, however, that this means the value must be an int
. As before, you must do open Base.Poly
to fix this before defining the function to make the comparison polymorphic again:
val find_mismatches :
('a, 'b) Core.Hashtbl.t -> ('a, 'b) Core.Hashtbl.t -> 'a list = <fun>
As to why this was happening, I’m more sure, but if you see this, you may need to do something similar.
S-expressions
The example using Core.Time.sexp_of_t
no longer works: this function is deprecated:
utop # Time.sexp_of_t;;
Line 1, characters 0-14:
Alert deprecated: Core.Time.sexp_of_t
[since 2021-03] Use [Time_unix]
Line 1, characters 0-14:
Alert deprecated: Core.Time.sexp_of_t
[since 2021-03] Use [Time_unix]
- : [ `Use_Time_unix ] = `Use_Time_unix
Unfortunately, Time_unix
provided by unix-time doesn’t have this functionality. core_unix does have this functionality. Once you have that installed, try the following:
utop # #require "core_unix.time_unix";;
utop # Time_unix.sexp_of_t;;
Time_unix.t -> Sexplib0.Sexp.t = <fun>
utop # Error.create "Something failed a long time ago" Time.epoch Time_unix.sexp_of_t;;
- : Error.t =
("Something failed a long time ago" (1970-01-01 01:00:00.000000+01:00))
The <:sexp_of<...>>
extended syntax no longer seems to work. You instead need to use ppx_jane
for this and the [%sexp ...]
syntax. For instance:
let custom_to_sexp = <:sexp_of<float * string list * int>>
Becomes:
utop # #require "ppx_jane";;
utop # [%sexp (3.5, ["a"; "b"; "c"], 6034)];;
- : Sexp.t = (3.5 (a b c) 6034)
utop # Error.create "Something went wrong" [%sexp (3.5, ["a"; "b"; "c"], 6034)] Fn.id;;
- : Error.t = ("Something went wrong" (3.5 (a b c) 6034))
Fn.id
is the function fun x -> x
: Error.create
takes a third argument that can manipulate the second argument passed in. This would also work, and would typecheck the second argument:
utop # Error.create "Something went wrong" (3.5, ["a"; "b"; "c"], 6034) [%sexp_of: (float * string list * int)];;
- : Error.t = ("Something went wrong" (3.5 (a b c) 6034))
“bind and Other Error Handling Idioms”
Congratulations! You’re being introduced to monads! Specifically, this is the Maybe monad. Here’s a modified version of the function with the expected labels:
let compute_bounds_monadic ~compare list =
let sorted = List.sort ~compare list in
Option.bind (List.hd sorted) ~f:(fun first ->
Option.bind (List.last sorted) ~f:(fun last ->
Some (first, last)))
“Deriving exceptions using sexp”
Rather than:
exception Wrong_date of Date.t with sexp
Use an annotation:
exception Wrong_date of Date.t [@@deriving sexp]
“Cleaning Up in the Presence of Exceptions”
load_reminders
becomes:
let load_reminders filename =
let inc = In_channel.create filename in
let reminders = [%of_sexp: ((Time_unix.t * string) list)] (Sexp.input_sexp inc) in
In_channel.close inc;
reminders;;
Note that you’ll need require the ppx_jane
library for %of_sexp
, core_unix.time_unix
for Time_unix
.