Reputation: 453
How to implement mixins in the OCaml module system.
The closest I achieved is to use the following methodology, which I illustrate on a MixinFormat
which adds usual output functions print
, output
and to_string
on a mixin able to format
.
Assume that 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 common output functions to a module able to 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 very well but has the convenient that it is not easily iterable: if a second mxin expects functions added to Prototype
by MixinFormat.Make
then we need to pack Prototype
and MixinFormat.Make(Prototype)
in Prototype2
which is a bit clumsy and hinders readability.
Is there an alternative implementation of mixins which could avoid to introduce a Prototype2
when iteratively using mixins?
Upvotes: 4
Views: 568
Reputation: 1151
Firstly, to avoid multiple definitions of the type t
, your functor should probably be defined as:
module Make(B:Basis): Methods with type t := B.t
To avoid having to create 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 is worth noting that recursive modules can be a bit tricky. For example, if any of the values in your module (e.g. format
) were not functions then this definition would not work.
It is also worth noting that OCaml's object system has excellent support for mixins. For examples, see Chapter 12 of Real World OCaml.
Upvotes: 2