fullstack

OCaml

The OCaml cheat sheet is a one-page reference sheet for the OCaml programming language.

#Getting Started

#hello.ml

let () =
  let message = "Hello, World!" in
  Printf.printf "%s\n" message

#Compile and Run

$ ocamlc -o hello.byte hello.ml
$ ./hello.byte

#Build and Run with Dune

$ dune build hello.exe
$ _build/default/hello.exe

# you can also just run
$ dune exec ./hello.exe

See the dune documentation for more info.

#Imports

Install modules with opam

$ opam install hex

#Global Open

open Hex 

#Local 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 404

#Arrays & 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 = 2

#Multiple parameters

let sum x y =
  let result = x + y in
  result

# sum 1 2;;
- : int = 3

#Tuple 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 1

#Chaining

#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 i 
done

#While 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
  done

#Operators

#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 lst

#Associaton Lists

#Definition and Access

let scores = 
  [("math", 91); ("phil", 89); ("stats", 94)]

# List.assoc "stats" scores;;
- : int = 94

# List.mem_assoc "math" scores;;
- : bool = true

#Split 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) books

#Printing 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