OCaml
Getting Started
hello.ml
let () = let message = "Hello, World!" in Printf.printf "%s\n" messageCompile and Run
$ ocamlc -o hello.byte hello.ml$ ./hello.byteBuild and Run with Dune
$ dune build hello.exe$ _build/default/hello.exe
# you can also just run$ dune exec ./hello.exeSee the dune documentation for more info.
Imports
Install modules with opam
$ opam install hexGlobal Open
open HexLocal Open
Hex.of_string "hex string"
let open Hex in of_string "to hex"Comments
Line & Block Comments
(* A single line comment *)
(* A multi-line comment* where we want to explain* something complex *)
(* Outer comment (* Nested comment *) End of outer comment *)Documentation Comments
val sum : int -> int -> int(** [sum x y] returns the sum of two integers [x] and [y] *)Data Types
Predefined Types
Unit
Note: # Indicates execution at the toplevel followed by the output
# ();; (* equivalent to void in C *)- : unit = ()Basic Types
# 5 ;; (* integer *)- : int = 5
# 3.14 ;; (* float *)- : float = 3.14
# true ;; (* bool *)- : bool = true
# false ;;- : bool = false
# 'a' ;; (* char *)- : char = 'a'
# "a string" ;; (* string *)- : string = "a string"
# String.to_bytes "hello" ;; (* bytes *)- : bytes = Bytes.of_string "hello"
# (3, 5);; (* tuple *)- : int * int = (3, 5)
# ref 0;; (* reference *)- : int ref = {contents = 0}Options & Results
# Some 42;;- : int option = Some 42
# Ok 42;;- : (int, 'a) result = Ok 42
# Error "404";;- : ('a, int) result = Error 404Arrays & Lists
Arrays
# [|0; 1; 2; 3|];; (* create an array *)- : int array = [|0; 1; 2; 3|]
# [|'u'; 's'; 'c'|].(1);; (* array access *)- char = 's'Arrays are mutable
let scores = [|97; 85; 99|];;- : int array = [|97; 85; 99|]
# scores.(2) <- 89;; (* update an element *)- unit = ()
# scores;;- : int array = [|97; 85; 89|]Lists
# [1; 2; 3];;- : int list = [1; 2; 3;]
# ["a"; "str"; "lst"];;- : string list = ["a"; "str"; "lst"]Lists are immutable
# let lst = [1; 2; 3];;- : int list = [1; 2; 3]
# let new_lst = 0 :: lst;; (* prepend to a new list *)- : int list = [0; 1; 2; 3]
# new_lst @ [4;5;6];; (* combine two lists *)- : int list = [0; 1; 2; 3; 4; 5; 6]User-Defined Types
Records
Bundle related data
type person = { name: string; age: int}
# let zeno = {name = "Zeno"; age = 30};;val zeno : person = {name = "Zeno"; age = 30}Variants
Several different, but related types
type shape = | Circle of float | Rectangle of float * float
# let my_shape = Circle 5.0;;- : shape = Circle 5.Aliases
Provide meaningful name to complex or commonly used types
type point = float * float
# let origin: point = (0.0, 0.0);;val origin : point = (0., 0.)Functions
Functions
Single parameter
let add_one x = let result = x + 1 in result
# add_one 1;;- : int = 2Multiple parameters
let sum x y = let result = x + y in result
# sum 1 2;;- : int = 3Tuple parameter
let str_concat (x, y) = x ^ " " ^ y
# str_concat ("Hello", "OCaml") ;;- : string = "Hello Ocaml"Recursive Functions
rec keyword
All recusive functions use the rec keyword
let rec factorial n = if n < 1 then 1 else n * factorial (n - 1)The above can cause stackoverflow.
Tail Recursion
Makes use of a helper function and the acc argument.
let rec factorial_helper n acc = if n = 0 then acc else factorial_helper (n - 1) (n * acc)Notice the last call is the recursive function.
let factorial n = factorial_helper n 1Chaining
Application Operator
Read from right to left, the first operation is sum 2 3
(* find log(2 + 3) *)# log @@ float_of_int @@ sum 2 3 ;;- : float = 1.609...Pipeline
(* find log((x + y)!) *)# sum 2 3 |> factorial |> float_of_int |> log ;;- : float = 4.787...|> takes the output of the function and passes it as input to the next function in the pipeline
Control Flow
If Statements
If Statement
let is_pos x = if x > 0 then "positive" else "negative"If else if
let f x = if x > 3 then "gt 3" else if x < 3 then "lt 3" else "eq 3"Pattern Matching
let is_pos x = match x > 0 with | true -> "positive" | false -> "negative"Loops
For loop
for i = 1 to 5 do print_int idoneWhile loop
Notice the ref is needed to have the while condition eventually become false.
let i = ref 0 in while !i < 5 do print_int !i; i := !i + 1 doneOperators
Comparison Operators
= (* equal to *)<> (* not equal to *)> (* greater than *)< (* less than *)>= (* greater than or eq to *)<= (* less than or eq to *)Arithmatic Operators
(* int operator float operator *)+ +. (* addition *)- -. (* subtraction *)* *. (* multiplication *)/ /. (* division *) ** (* power *)Useful Tools
List
Searching & Filtering
# let lst = [1; 2; 3];;val lst : int list = [1; 2; 3]
# List.filter (fun x -> x mod 2 = 0) lst;;- : int list = [2]
# List.find (fun x -> x = 4) lst;;Exception: Not_found
# List.sort compare [2; 1; 3];;- : int list = [1; 2; 3]Applying Transformations
(* Loop over list and apply fun f *)List.iter f lst
(* map a function to each elem *)(* Ex. double each element x in lst *)List.map (fun x -> x + x) lst
(* Apply an operator between elements *)(* Ex. 1 + 2 + 3 *)List.fold_left (+) 0 lstAssociaton Lists
Definition and Access
let scores = [("math", 91); ("phil", 89); ("stats", 94)]
# List.assoc "stats" scores;;- : int = 94
# List.mem_assoc "math" scores;;- : bool = trueSplit and Combine
# List.split scores;;- : string list * int list = (["math"; "phil"; "stats"], [91; 89; 94])
# List.combine [1;2;3] [4; 5; 6];;- : (int * int) list = [(1, 4); (2, 5); (3, 6)]Association lists are similar to dictionaries or hashmaps
Hash Tables
Hash Tables are mutable.
Initialize & Add Data
# let my_htable = Hashtbl.create 3;;val my_htable : ('_weak1, '_weak2) Hashtbl.t = <abstr>
# Hashtbl.add my_htable "A" "John"; Hashtbl.add my_htable "A" "Jane"; Hashtbl.add my_htable "B" "Max";;Find Data
# Hashtbl.find my_htable "A";;- : string = "Jane"
(* find all *)# Hashtbl.find_all my_htable "A";;- : string list = ["Jane"; "John"]Maps
Maps are immutable key-value association tables.
Initialization & Add Data
(* the Map.Make functor creates the custom map module *)# module StringMap = Map.Make(String);;
let books = StringMap.empty |> StringMap.add "Dune" ("Herbet", 1965) |> StringMap.add "Neuromancer" ("Gibson", 1984)Finding Entries
(* find_opt returns assoc val wrapped in an option else None *)# StringMap.find_opt "Dune" books;;- : (string * int) option = Some ("Herbet", 1965)
(* find returns the association else Not_Found *)# StringMap.find "Dune" books;;- : string * int = ("Herbet", 1965)Adding & Removing Entries
Creates a new map since maps are immutable
let more_books = books |> StringMap.add "Foundation" ("Isaac Asimov", 1951)
let less_books = |> StringMap.remove "Dune"Filtering
let eighties_books = StringMap.filter (fun _ (_, year) -> year > 1980 & number < 1990) booksPrinting Data
let print_books map = StringMap.iter (fun title (author, year) -> Printf.printf "Title: %s, Author: %s, Year: %d\n" title author year ) map
# let () = print_books eighties_books;;Title: Neuromancer, Author: Gibson, Year: 1984