Real World OCaml
ISBN 978-7-5123-7637-3
Install Guidence from cornell/cs3110
Part I
based on Core instead of standard library
1 / 3;;
(* - : int = 0 *)
1 / 3.;;
1. /. 3;;
2. / 3.;;
(* Error: This expression has type float but an expression was expected of type int *)
1. /. 3.;;
(* - : float = 0.333333333333333315 *)
let even x =
x mod 2 = 0 ;;
(* val even : int -> bool = <fun> *)
Identifiers for variable names:
punctuation is excluded, except for _ and ', must start with a lowercase letter or an underscore.
By default, utop doesn’t print out variables starting with an underscore.
first-class functions
Module names
always start with a capital letter.
type inference
let sum_if_true test first second =
(if test first then first else 0)
+ (if test second then second else 0)
;;
(* val sum_if_true : (int -> bool) -> int -> int -> int = <fun> *)
let sum_if_true (test : int -> bool) (x : int) (y : int) : int =
(if test x then x else 0)
+ (if test y then y else 0)
;;
parametric polymorphism
generic type & type variable
let first_if_true test x y =
if test x then x else y
;;
(* val first_if_true : ('a -> bool) -> 'a -> 'a -> 'a = <fun> *)
* compile time error & runtime exception
built-in simple types:
int, float, bool, char, string, unit
pattern matching
let t = (3, "four", 5.);;
(* val t : int * string * float = (3, "four", 5.) *)
let (x, y, z) = t;;
* tuple: fixed item number, different types
* list: any item number, the same type
~f: labeled argument
let langs = ["ocaml", "cpp", "c"];;
(* a singleton list containing a three-tuple
* val langs : (string * string * string) list = [("ocaml", "cpp", "c")]
*)
let langs = ["ocaml"; "cpp"; "c"];;
(* val langs : string list = ["ocaml"; "cpp"; "c"] *)
List.map langs ~f:String.length;;
(* - : int list = [5; 3; 1] *)
"python" :: langs;;
(* - : string list = ["python"; "ocaml"; "cpp"; "c"] *)
langs;;
(* - : string list = ["ocaml"; "cpp"; "c"] *)
["py"; "perl"] @ langs;;
(* - : string list = ["py"; "perl"; "ocaml"; "cpp"; "c"] *)
The bracket notation for lists is really just syntactic sugar for :: (right-associative).
Unlike ::, @ is not a constant-time operation (proportional to the length of the first list).
let my_fav_lang (my_fav :: the_rest) = my_fav;;
(* Here is an example of a case that is not matched: [] *)
let my_fav_lang langs =
match langs with
| first :: the_rest -> first
| [] -> "ocaml"
;;
recursive
let rec sum l =
match l with
| [] -> 0
| hd :: tl -> hd + sum tl
;;
option
let divide x y =
if y = 0 then None else Some (x/y) ;;
(* val divide : int -> int -> int option = <fun> *)
A let paired with an in can be used to introduce a new binding within any local scope, including a function body.
The in marks the beginning of the scope within which the new variable can be used.
let x = 7 in
let y = x * x in
x + y
;;
record
type point2d = {x : float; y : float};;
let p = {x = 3.; y = -4.};;
(* val p : point2d = {x = 3.; y = -4.} *)
variant
type v =
| Int of int
| Point2D of point2d
;;
(* type v = Int of int | Point2D of point2d *)
array
let numbers = [| 1; 2; 3; 4 |];;
(* val numbers : int array = [|1; 2; 3; 4|] *)
numbers.(2) <- 4;;
(* - : unit = () *)
The unit type has only one possible value, written (), generally used as a placeholder.
We use unit for the return value of an operation like setting a mutable field that communicates by side effect rather than by returning a value. It’s also used as the argument to functions that don’t require an input value (like void in C or Java).
mutable record
type running_sum =
{
mutable sum : float;
mutable samples : int;
};;
let mean rsum = rsum.sum /. float rsum.samples
let create () = {sum = 0.; samples = 0}
let update rsum x =
rsum.samples <- rsum.samples + 1;
rsum.sum <- rsum.sum +. x
;;
(* val mean : running_sum -> float = <fun>
* val create : unit -> running_sum = <fun>
* val update : running_sum -> float -> unit = <fun>
*)
let rsum = create ();;
List.iter [1.;3.;2.;-7.;4.;5.] ~f:(fun x -> update rsum x);;
mean rsum;;
Anonymous functions
are declared using the fun keyword, and don’t need to be ex‐ plicitly named.
ref:
the standard way of simulating the traditional mutable variables
let x = {contents = 0};;
x.contents <- x.contents + 1;;
(* equivalent to *)
let x = ref 0;;
x := !x + 1;;
(* implementations *)
type 'a ref = {mutable contents : 'a}
let ref x = {contents = x}
let (!) r = r.contents
let (:=) r x = r.contents + x
;;
The parentheses around ! and := are needed because these are operators, rather than ordinary functions.
let sum list =
let sum = ref 0 in
List.iter list ~f:(fun x -> sum := !sum + x);
!sum
;;
for & while
out of toplevel
(* sum.ml *)
open Core
let rec read_and_accumulate accum =
let line = In_channel.input_line In_channel.stdin in
match line with
| None -> accum
| Some x -> read_and_accumulate (accum +. Float.of_string x)
let () =
printf "Total: %F\n" (read_and_accumulate 0.)
corebuild sum.ml
Part II
anonymous & higer-order function
let increments = [ (fun x -> x + 1); (fun x -> x + 2) ];;
(* val increments : (int -> int) list = [<fun>; <fun>] *)
List.map ~f:(fun g -> g 5) increments;;
(* - : int list = [6; 7] *)
currying
let abs_diff x y = abs (x - y);;
(* equivalent to *)
let abs_diff =
(fun x -> (fun y -> abs (x - y)));;
(* partial application *)
let dist_from_3 = abs_diff 3;;
or pattern
| [] | [_]
Explicitly marking of recursion doesn’t apply to a pure language like Haskell.
Predetermined identifiers characters set and strings:
! $ % & * + - . / : < = > ? @ ^ | ~
or mod lsl (logical shift left)
(+) 1 2;;
(* - : int = 3 *)
Int.max 3 -4;; (* error *)
Int.max 3 (-4);;
let (|>) x f = f x;;
(* val ( |> ) : 'a -> ('a -> 'b) -> 'b = <fun> *)
Negation has lower precedence than function application.
function keyword
let some_or_zero function
| Some x -> x
| None -> 0
;;
__labeled argument __
let ratio ~num ~denom = float num /. float denom;;
(* val ratio : num:int -> denom:int -> float = <fun> *)
ratio ~denom:10 ~num:3;;
Label punning:
you get to drop the text after the : if the name of the label and the name of the variable being used are the same.
TODO
- files
- modules
- functors
- objects
- classes
Part III
TODO
- s-expression
- concurrency
- FFI
- GC
- compiler frontend