Functors which are not functors
The Ocaml module system
I've always been jealous of ML's module system
— Simon Peyton Jones
Elements of Ocaml syntax
Typing with a colon
i : int
Type argument order
(** Also the constructor for list is double-colon *)
( :: ) : 'a -> 'a list -> 'a list
Modules and signatures
module Fixed = struct
let scale = 8
let of_int i = i lsl scale
let half = 1 lsl (scale - 1)
(* note: not the same (+). *)
let (+) = (+)
let ( * ) n p = (n*p) asr scale
end
module Fixed : sig
(** [of_int i] is [i] as a fixed point
number. *)
val of_int : int -> int
(** [half] is the number 1/2. *)
val half : int
(** [n+p] is the sum of [n] and [p], this
operation is exact. *)
val (+) : int -> int -> int
(** [n*p] is the product of [n] and [p],
rounded down. *)
val ( * ) : int -> int -> int
end
let x = Fixed.half
open Fixed
let y = half * half + (Fixed.of_int 1)
Files as implicit modules
let scale = 8
let of_int i = i lsl scale
let half = 1 lsl (scale - 1)
(* note: not the same (+). *)
let (+) = (+)
let ( * ) n p = (n*p) asr scale
(** [of_int i] is [i] as a fixed point
number. *)
val of_int : int -> int
(** [half] is the number 1/2. *)
val half : int
(** [n+p] is the sum of [n] and [p], this
operation is exact. *)
val (+) : int -> int -> int
(** [n*p] is the product of [n] and [p],
rounded down. *)
val ( * ) : int -> int -> int
fixed.mli
fixed.ml
let x = Fixed.half
open Fixed
let y = half * half + (Fixed.of_int 1)
Submodules
let scale = 8
let of_int i = i lsl scale
let half = 1 lsl (scale - 1)
(* note: not the same (+). *)
let (+) = (+)
let ( * ) n p = (n*p) asr scale
module Repr = struct
let integer_part n = n asr scale
let fractional_part n =
n land (lnot (-1 lsl scale))
end
val of_int : int -> int
val half : int
val (+) : int -> int -> int
val ( * ) : int -> int -> int
module Repr : sig
(** [integer_part n] is the integer part
of the fixed point number [n]. *)
val integer_part : int -> int
(** [fractional_part n] is the fractional
part of the fixed point number [n]
multiplied by 2^8.*)
val fractional_part : int -> int
end
fixed.mli
fixed.ml
let x =
Fixed.Repr.integer_part (Fixed.of_int 57)
open Fixed
let y = Repr.fractional_part (of_int 18)
open Fixed.Repr
let y = integral_part (Fixed.of_int 42)
Abstract types
type t = int
let scale = 8
let of_int i = i lsl scale
let half = 1 lsl (scale - 1)
(* note: not the same (+). *)
let (+) = (+)
let ( * ) n p = (n*p) asr scale
(** Fixed point numbers with scaling
factor 2^8*)
type t
(** [of_int i] is [i] as a fixed point
number. *)
val of_int : int -> t
(** [half] is the number 1/2. *)
val half : t
(** [n+p] is the sum of [n] and [p], this
operation is exact. *)
val (+) : t -> t -> t
(** [n*p] is the product of [n] and [p],
rounded down. *)
val ( * ) : t -> t -> t
fixed.mli
fixed.ml
let x : Fixed.t = Fixed.half
open Fixed
let y = half * half + (Fixed.of_int 1)
Local opens (=import)
(** Fixed point numbers with scaling
factor 2^8*)
type t
(** [of_int i] is [i] as a fixed point
number. *)
val of_int : int -> t
(** [half] is the number 1/2. *)
val half : t
(** [n+p] is the sum of [n] and [p], this
operation is exact. *)
val (+) : t -> t -> t
(** [n*p] is the product of [n] and [p],
rounded down. *)
val ( * ) : t -> t -> t
fixed.mli
let y =
Fixed.(+)
(Fixed.(*)
Fixed.half
Fixed.half)
(Fixed.of_int 1)
let y =
Fixed.(half * half + (Fixed.of_int 1))
(* equivalently *)
let y =
let open Fixed in
half * half + (Fixed.of_int 1)
vs.
Functors
module type Scale = sig
(** Scaling factor for fixed point numbers
expressed as a binary logarithm. *)
val scale : int
end
module Make (S : Scale) : sig
(** Fixed point numbers with scaling
factor 2^8*)
type t
val of_int : int -> t
val half : t
val (+) : t -> t -> t
val ( * ) : t -> t -> t
end
fixed.ml
fixed.mli
So, I hear you like dependent types
module type Scale = sig
val scale : int
end
module Make (S : Scale) = struct
type t = int
let of_int i = i lsl S.scale
let half = 1 lsl (S.scale - 1)
let (+) = (+)
let ( * ) n p = (n*p) asr S.scale
end
module Fixed8 =
Fixed.Make(struct scale = 8 end)
let y =
Fixed8.(half * half + (Fixed8.of_int 1))
More functors
module type Integer = sig
type t
val of_int : int -> t
val (+) : t -> t -> t
val ( * ) : t -> t -> t
val (lsl) : t -> int -> t
val (asr) : t -> int -> t
…
module Carry : sig
val plus : t -> t -> bool*t
val times : t -> t -> t*t
end
end
module type Scale = sig
val scale : int
end
module Fixed (I : Integer) (S : Scale) : sig
(** Fixed point numbers with scaling
factor 2^8*)
type t
val of_int : int -> t
val half : t
val (+) : t -> t -> t
val ( * ) : t -> t -> t
end
module type Integer = …
module type Scale = …
module Fixed (I : Integer) (S : Scale) : sig
(** Fixed point numbers with scaling
factor 2^8*)
type t
val of_int : int -> t
val half : t
val (+) : t -> t -> t
val ( * ) : t -> t -> t
end
module Int : Integer
module DoubleInt (I : Integer) : Integer
module Fixed16 =
Fixed.Make
(DoubleInt(Int))
(struct scale = 16 end)
let y =
Fixed16.(half * half + (Fixed16.of_int 1))
(** Almost complete implementation *)
module type Integer = sig
type t
val of_int : int -> t
val (+) : t -> t -> t
val ( * ) : t -> t -> t
val (lsl) : t -> int -> t
val (asr) : t -> int -> t
…
module Carry : sig
val plus : t -> t -> bool*t
val times : t -> t -> t*t
end
end
module type Scale = sig
val scale : int
end
module Int : Integer = struct
type t = int
let of_int i = i
let (+) = (+)
let ( * ) = ( * )
let (lsl) = (lsl)
let (asr) = (asr)
module Carry = struct
let plus n p = …
let times n p = …
end
end
module DoubleInt (I : Integer) : Integer = struct
type t = I.t * I.t
let of_int i = I.(of_int 0, of_int i)
module Carry = struct
let plus (nh,nl) (ph,pl) =
let (carryl,low) = I.Carry.plus nl pl in
let (carryh,high) =
if carryl then
I.Carry.plus nh ph
else
let (carry,h) = I.Carry.plus nh ph in
(carry, I.(h + of_int 1))
in
(carryh, (high,low))
let (+) n p = snd (plus n p)
(* Karatsuba multiplication. *)
let times (nh,nl) (ph,pl) =
let h = I.Carry.times nh ph in
let l = I.Carry.times nl pl in
let (mc,(mh, ml)) =
plus
(I.Carry.times nh pl)
(I.Carry.times nl ph)
in
let mcw = I.of_int (if mc then 1 else 0) in
let (c,rl) = plus (ml,I.of_int 0) l in
let rh =
if c then
h + (mh, mcw)
else
h + (mh, mcw) + of_int 1
in
( rh , rl )
end
let (+) = Carry.(+)
let ( * ) n p = snd (Carry.times n p)
let (lsl) (nh,nl) k = …
let (asr) (nh,nl) k = …
…
end
module Fixed (I : Integer) (S : Scale) : sig
(** Fixed point numbers with scaling
factor 2^8*)
type t
val of_int : int -> t
val half : t
val (+) : t -> t -> t
val ( * ) : t -> t -> t
end = struct
type t = I.t
let of_int i = I.(of_int i lsl S.scale)
let half = I.(of_int 1 lsl (S.scale - 1))
let (+) = I.(+)
let ( * ) n p = I.((n*p) asr S.scale)
end
More
- Manifest dependent types (with)
- (Mutually) recursive modules
- First-class modules
- Private types
- Generative exceptions/constructors
Modules in Haskell
Research
Backpack: system of mixins at the library level
In 8.4
Functors which are not functors
By Arnaud Spiwack
Functors which are not functors
The ML family of programming languages have, basically forever, come with a sweet module system (including functors which are not, well, functors, in the mathematical sense; no, seriously, they are completely unrelated, except for the name). I want to run you through a few feature of Ocaml's module system. This includes the aforementioned functors, first-class modules, as well as local namespace imports, which I really love. And why I miss them when I program in Haskell. I'll start with a typology of module and namespace systems. And we'll touch on how differently type-classes and ML functors solve the same problem.
- 929