Example: Natural Numbers

3.12. Example: Natural Numbers#

We can define a recursive variant that acts like numbers, demonstrating that we don’t really have to have numbers built into OCaml! (For sake of efficiency, though, it’s a good thing they are.)

A natural number is either zero or the successor of some other natural number. This is how you might define the natural numbers in a mathematical logic course, and it leads naturally to the following OCaml type nat:

type nat = Zero | Succ of nat
type nat = Zero | Succ of nat

We have defined a new type nat, and Zero and Succ are constructors for values of this type. This allows us to build expressions that have an arbitrary number of nested Succ constructors. Such values act like natural numbers:

let zero = Zero
let one = Succ zero
let two = Succ one
let three = Succ two
let four = Succ three
val zero : nat = Zero
val one : nat = Succ Zero
val two : nat = Succ (Succ Zero)
val three : nat = Succ (Succ (Succ Zero))
val four : nat = Succ (Succ (Succ (Succ Zero)))

Now we can write functions to manipulate values of this type. We’ll write a lot of type annotations in the code below to help the reader keep track of which values are nat versus int; the compiler, of course, doesn’t need our help.

let iszero = function
  | Zero -> true
  | Succ _ -> false

let pred = function
  | Zero -> failwith "pred Zero is undefined"
  | Succ m -> m
val iszero : nat -> bool = <fun>
val pred : nat -> nat = <fun>

Similarly, we can define a function to add two numbers:

let rec add n1 n2 =
  match n1 with
  | Zero -> n2
  | Succ pred_n -> add pred_n (Succ n2)
val add : nat -> nat -> nat = <fun>

We can convert nat values to type int and vice-versa:

let rec int_of_nat = function
  | Zero -> 0
  | Succ m -> 1 + int_of_nat m

let rec nat_of_int = function
  | i when i = 0 -> Zero
  | i when i > 0 -> Succ (nat_of_int (i - 1))
  | _ -> failwith "nat_of_int is undefined on negative ints"
val int_of_nat : nat -> int = <fun>
val nat_of_int : int -> nat = <fun>

To determine whether a natural number is even or odd, we can write a pair of mutually recursive functions:

let rec even = function Zero -> true | Succ m -> odd m
and odd = function Zero -> false | Succ m -> even m
val even : nat -> bool = <fun>
val odd : nat -> bool = <fun>