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?

+3


source to share


1 answer


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 .

+1


source







All Articles