How to implement mixins in OCaml
How to implement mixins in OCaml module system.
The closest I have achieved is to use the following methodology, which I illustrate with MixinFormat
, which adds the usual output functions print
, output
and to_string
a mixin capable of format
.
Let's assume we implement a module MixinFormat
with the following signature:
module MixinFormat :
sig
(** Formatable mixin. *)
(** Input signature of the functor [MixinFormat.Make]. *)
module type Basis =
sig
type t
(** The type of formatted elements. *)
val format : Format.formatter -> t -> unit
(** [format fft a] pretty prints [a] on [fft]. *)
end
(** Output signature of the functor [MixinFormat.Make]. *)
module type Methods =
sig
type t
val to_string : t -> string
(** Convert to string. *)
val output : out_channel -> t -> unit
(** Output on the given output channel. *)
val print : t -> unit
(** Output on the standard output channel. *)
end
(** Functor implementing output mixins based on a format definition. *)
module Make(B:Basis): Methods
with type t := B.t
(** Signature of formatable mixins. *)
module type S =
sig
type t
include Basis with type t := t
include Methods with type t := t
end
end
We can now use it to add general inference functions to a module capable of format
, as in:
module Date =
struct
module Prototype =
struct
type t = int * int * int
let format ppt (y,m,d) =
Format.fprintf ppt "%04d-%02d-%02d" y m d
end
include Prototype
include MixinFormat.Make(Prototype)
end
This works really well, but has the convenience of being a tricky task: if the second mxin is expecting features added in Prototype
on MixinFormat.Make
, then we need to package in Prototype
and MixinFormat.Make(Prototype)
in Prototype2
, which is a little clunky and difficult to read.
Is there an alternative mixins implementation that could avoid the introduction Prototype2
when using mixins iteratively?
source to share
First, to avoid multiple type definitions t
, your functor should probably be defined as:
module Make(B:Basis): Methods with type t := B.t
To avoid creating inner modules, you can use recursive modules like this:
module rec Date : sig
type t = int * int * int
include Basis with type t := t
include Methods with type t := t
end = struct
type t = int * int * int
let format ppt (y,m,d) =
Format.fprintf ppt "%04d-%02d-%02d" y m d
include Make(Date)
end
However, it's worth noting that recursive modules can be a little tricky. For example, if any of the values in your module (for example format
) were not functions, this definition will not work.
It's also worth noting that the OCaml object system has excellent support for mixins. For example, see Chapter 12 of the OCaml Real World .
source to share