OCaml modules: combining (interconnected) types from different modules into a new module

Problem

One of the problems I am facing is casting the types and vals of two modules into a new combined module. I will give an example. I currently have the following two type signatures

module type Ordered = sig type t (* the type of elements which have an order *) val eq : t * t -> bool val lt : t * t -> bool val leq : t * t -> bool end module type Stack = sig exception Empty type 'at (* the type of polymorphic stacks *) val empty : 'at val isEmpty : 'at -> bool val cons : 'a * 'at -> 'at val head : 'at -> 'a val tail : 'at -> 'at end 

and I would like to create a module of "stacks for which the main elements are ordered", i.e.

 module type OrderedStack = sig exception Empty type elem (* the type of the elements in the stack *) val eq : elem * elem -> bool val lt : elem * elem -> bool val leq : elem * elem -> bool type t (* the type of monomorphic stacks *) val empty : t val isEmpty : t -> bool val cons : elem * t -> t val head : t -> elem val tail : t -> t end 

So far, everything is beautiful and neat. But now I would like to write a functor that takes the Ordered module and the Stack module and creates the OrderedStack module. Something like

 module My_functor (Elem : Ordered) (St : Stack): OrderedStack = struct exception Empty type elem = Elem.t let eq = Elem.eq let lt = Elem.lt let leq = Elem.leq type t = elem St.t let empty = St.empty let isEmpty = St.isEmpty let cons = St.cons let head = St.head let tail = St.tail end 

This is exactly what I want and right. But it looks like a terrible waste of keyboard.

My question

Is there a more compact way to write My_functor above?

What I found out, but could not work

I saw an include directive in which I could write something like:

 module my_functor (Elem : Ordered) (St : Stack): OrderedStack = struct include Elem include St end 

but this is due to the fact that for my two above modules both ordered and stacks have the same type t (although they are different in each of them). I would prefer not to change the original definition of Ordered and Stacks , as they are already used in many parts of the code, but if you find an alternative formulation for the original two modules that make it work, that's fine.

I also saw that the with statement could make a difference here, but I couldn't fully understand how to use it to get the desired effect. The problem I am facing is that the types t and 'at two modules Ordered and Stacks are actually connected.

Any ideas?

+7
source share
1 answer

Reusing OrderedStack Ordered definitions with a slightly different type ( elem instead of t ). This is the cause of redundancy.

Instead, you can use this OrderedStack signature, which will be reused by Ordered :

 module type OrderedStack = sig module Elem : Ordered type t val empty : t val isEmpty : t -> bool val cons : Elem.t * t -> t val head : t -> Elem.t val tail : t -> t end 

Another source of redundancy is the fact that you are switching from the parametric type 'a Stack.t to the monomorphic OrderedStack.t . These two types cannot be identified, they are not at all comparable, so you must definitely do the translation manually.

Note that you can decompose the transition from (polymorphic) Stack to OrderedStack into one intermediate stack (monomorphic) MonoStack :

 module type MonoStack = sig type elem type t val empty : t val isEmpty : t -> bool val cons : elem * t -> t val head : t -> elem val tail : t -> t end module type OrderedStack = sig module Elem : Ordered module Stack : MonoStack with type elem = Elem.t end 

Edit

If you do not like the additional indirect use of submodules, which can add a syntactic burden, it is possible to include modules instead of binding them. But the problem, as you noticed, is a name conflict. Starting with OCaml 3.12, we have a new design in our toolbox that allows you to rename components such as signatures to avoid conflicts.

 module type OrderedStack = sig type elem include Ordered with type t := elem include MonoStack with type elem := elem end 

Second edit

Ok, I came up with the following solution to bring the Stack / MonoStack . But to be honest, it's a hack, and I don't think this is a good idea.

 module type PolyOrderedStack = sig module Elem : Ordered type t type 'a const = t module Stack : Stack with type 'at = 'a const end (* 3.12 only *) module type PolyOrderedStack = sig type elem include Ordered with type t := elem type t type 'a const = t include Stack with type 'at := 'a const end 
+8
source

All Articles