OCaml has a similar feature to interfaces called signatures
module type Fact = sig
(** [fact n] is n factorial. *)
val fact : int -> int
end
module type Fact = sig val fact : int -> int end
Here we say "module type," because we specify what type a module has, rather than it's value. sig represents a signature. Here we specify that there must be a value called "fact," that takes an int and returns an int. It's the same type of signature that utop returns!
module RecursiveFact : Fact = struct
let rec fact n =
if n = 0 then 1 else n * fact (n - 1)
end
module RecursiveFact : Fact
Here, we implement RecursiveFact which is of type Fact. The typechecker makes sure that the types are correct.
module ImNotFact : Fact = struct
let printHello = print_endline "Hello"
end
File "[3]", lines 1-3, characters 26-3:
1 | ..........................struct
2 | let printHello = print_endline "Hello"
3 | end
Error: Signature mismatch:
Modules do not match:
sig val printHello : unit end
is not included in
Fact
The value `fact' is required but not provided
File "[1]", line 3, characters 1-22: Expected declaration
But you can have extra values!
module FactWithAddOne : Fact = struct
let fact = RecursiveFact.fact
let add_one = ( + ) 1
end
module FactWithAddOne : Fact
FactWithAddOne.add_one 1
File "[5]", line 1, characters 0-22:
1 | FactWithAddOne.add_one 1
^^^^^^^^^^^^^^^^^^^^^^
Error: Unbound value FactWithAddOne.add_one
You can have multiple modules that implement the same signature:
module TailRecursiveFact : Fact = struct
let rec fact_aux acc n =
if n = 0 then acc
else fact_aux (n * acc) (n - 1)
let fact = fact_aux 0
end
module TailRecursiveFact : Fact