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

Made with Slides.com