Skip to content

Instantly share code, notes, and snippets.

@rdavison
Created May 9, 2018 07:00
Show Gist options
  • Save rdavison/dff657f9193f72c3ba883af0c049e079 to your computer and use it in GitHub Desktop.
Save rdavison/dff657f9193f72c3ba883af0c049e079 to your computer and use it in GitHub Desktop.
open Printf
(* NOTE:
* 1) In OCaml syntax, function application is done with a space, for example:
* Java: f(x, y, z)
* OCaml: f x y z
* 2) types are read backwards, for example:
* `int list` is read as "list of ints"
* `(int, string) result is read as "result of ints and strings"
* 3) Generics types start with an apostrophe. For example:
* Java: List<T> Java: List<String>
* OCaml: 't list OCaml: string list
*)
(******************************************************************************)
(* This defines a result type that can be either Ok or Error.
* The 'a means that it's generic in the Ok case, and 'e means it's
* generic in the Error case
*)
type ('a, 'e) result =
| Ok of 'a
| Error of 'e
(* At a high level, this function applies f to a `result` type, but ONLY
* if it is in the Ok case!
*)
let bind f result =
match result with
| Ok a -> f a
| Error e -> Error e
(* A standard infix notation for the bind function *)
let ( >>= ) result f = bind f result
(******************************************************************************)
(* Create new type to represent errors *)
type math_error =
| Divide_by_zero
| Too_low
| Too_high
(* Create new type to represent math result. It is implemented in terms of
the ('a, 'e) result type defined above where:
'a = int
'e = math_error
*)
type math_result = (int, math_error) result
(* Boilerplate to-string function for math errors.
* This boilerplate can be eliminated with preprocessors, but I didn't
* want to introduce that complexity in this example.
*)
let string_of_math_error x =
match x with
| Divide_by_zero -> "Divide_by_zero"
| Too_low -> "Too_low"
| Too_high -> "Too_high"
(* More boilerplate *)
let string_of_math_result x =
match x with
| Ok n -> sprintf "Ok %d" n
| Error e -> sprintf "Error %s" (string_of_math_error e)
(******************************************************************************)
(* If dividing by zero, return an error.
* This fuction returns a value of type `math_result`
*)
let safeDivide x y =
if y = 0 then
Error Divide_by_zero
else
Ok (x / y)
(* If x is not between min and max, return an error.
* This function returns a value of type `math_result`
*)
let constrain x min max =
if x < min then
Error Too_low
else if x > max then
Error Too_high
else
Ok x
(******************************************************************************)
let main =
(* Silly program to do some silly math *)
let numerator = 3 * 4 * 5 in
let len = 10 in
(* Initialize an array of size 10. The callback function is given the index
* and has the responsibility of return the value for that index.
*)
let result_array =
Array.init len (fun i ->
(* I define our silly_number to be 1 less than the index... so
* -1, 0, 1, 2, 3, etc...
*)
let silly_number = i - 1 in
(* This is a really really interesting part of the code.
* This says:
* constrain the number between 0 and 5
* then
* divide the numerator by that constrained number
*
* NOTE:
* 1) constrain may fail!
* 2) the >>= operator is used to reach into the result
* 3) safeDivide may also fail
*)
let result =
constrain silly_number 0 5 >>= fun constrained_number ->
safeDivide numerator constrained_number
in
(* Print the element before returning *)
printf "%d: %s\n" i (string_of_math_result result);
(* Return the result of the computation back to Array.init *)
result
)
in
printf "---------------\n";
(* Now we have a value in scope called `result_array` which is of type
* math_result array, which is the same as saying
* (int, math_error) result array
*
* Let's write some code to add up all the successful results
*)
(* First create a reference value *)
let total = ref 0 in
(* OCaml has imperative loops if you really want *)
for i = 0 to (len - 1) do
(* Read the element *)
let elem = Array.get result_array i in
(* The element is of type `math_result` so we need to unpack it *)
match elem with
| Ok x ->
printf "adding %d to total\n" x;
(* The ! in !total is just a dereference operator to read the current
* value stored in !total.
*
* The := sets the value of total to the right hand side
*)
total := !total + x
(* If the error case is not included, then the compiler will get angry.
* OCaml forces us to be thorough
*)
| Error _ ->
printf "skipping error\n"
done;
(* Print the final total *)
printf "total = %d\n" !total
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment