Created
May 9, 2018 07:00
-
-
Save rdavison/dff657f9193f72c3ba883af0c049e079 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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