Adèle Blanc-Sec
Adèle Blanc-Sec

Reputation: 453

How to implement mixins in OCaml

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

Answers (1)

Leo White
Leo White

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

Related Questions