# Require that min, max, (-) is defined on 'a'

Starting out, we have:

``````module Rect  = struct
type 'a t = {
x0: 'a;
y0: 'a;
x1: 'a;
y1: 'a;
} [@@deriving sexp];;
``````

Now, I want to be able to define generic functions

``````left: Rect.t -> 'a
bot: Rect.t -> 'a
width: Rect.t -> 'a
height: Rect.t -> 'a
``````

this would require that we have (-), min, max defined on 'a.

How do I enforce this constraint ?

You could use a functor which requires a module implementing a signature that contains those 3 functions and a type. Similar to what is done with `Set` or `Map`.

Here is what I have so far:

``````module type My_Dim =
sig
type t [@@deriving sexp]
val (+): t -> t -> t
val (-): t -> t -> t
val min: t -> t -> t
val max: t -> t -> t
end

module Rect (D: My_Dim)  = struct
type t = {
x0: D.t;
y0: D.t;
x1: D.t;
y1: D.t;
} [@@deriving sexp];;

let min_x (r: t) = D.min r.x0 r.x1;;
let min_y (r: t) = D.min r.y0 r.y1;;
let width (r: t) = D.((max r.x0 r.x1) - (min r.x0 r.x1));;
let height (r: t) = D.((max r.y0 r.y1) - (min r.y0 r.y1));;

(*
let map (fx: 'a -> 'b) (fy: 'a -> 'b) (r: 'a t ) : 'b t  =
{ x0 = fx r.x0;
x1 = fx r.x1;
y0 = fy r.y0;
y1 = fy r.y1; }
*)
end
``````

I need some help with the commented out function. The idea I want to express is:

given D: My_Dim, D2: My_Dim, fx: D.t â†’ D2.t, fy: D.t â†’ D2.t, r: D Rect.t
we can produce a D2 Rect.t

Is the most elegant solution to this:

``````module type My_Dim =
sig
type t [@@deriving sexp]
val (+): t -> t -> t
val (-): t -> t -> t
val min: t -> t -> t
val max: t -> t -> t
end

module Rect (D: My_Dim)  = struct
type t = {
x0: D.t;
y0: D.t;
x1: D.t;
y1: D.t;
} [@@deriving sexp];;

let min_x (r: t) = D.min r.x0 r.x1;;
let min_y (r: t) = D.min r.y0 r.y1;;
let width (r: t) = D.((max r.x0 r.x1) - (min r.x0 r.x1));;
let height (r: t) = D.((max r.y0 r.y1) - (min r.y0 r.y1));;
end

module RectConv (D1: My_Dim) (D2: My_Dim) = struct
let map (fx: D1.t -> D2.t) (fy: D1.t -> D2.t) (r: Rect(D1).t ) : Rect(D2).t  =
{ x0 = fx r.x0;
x1 = fx r.x1;
y0 = fy r.y0;
y1 = fy r.y1; }
end
``````

This looks somewhat verbose for OCaml.

Yes, modules are unfortunately verbose in OCaml.

One possibility is to pass first-class modules as witnesses:

``````module type Dim = sig
type t

val ( + ) : t -> t -> t
val ( - ) : t -> t -> t
val min : t -> t -> t
val max : t -> t -> t
end

type 'a dim = (module Dim with type t = 'a)

module Dim = struct
let add (type a) ((module D) : a dim) x y : a = D.(x + y)
let sub (type a) ((module D) : a dim) x y : a = D.(x - y)
let min (type a) ((module D) : a dim) x y : a = D.min x y
let max (type a) ((module D) : a dim) x y : a = D.max x y
end

type 'a rect = { x0 : 'a; y0 : 'a; x1 : 'a; y1 : 'a }

let min_x d r = Dim.min d r.x0 r.x1
let max_x d r = Dim.max d r.x0 r.x1
let min_y d r = Dim.min d r.y0 r.y1
let max_y d r = Dim.max d r.y0 r.y1

let width (type a) (d : a dim) (r : a rect) =
let open (val d) in
max_x d r - min_x d r

let height d r = Dim.sub d (max_y d r) (min_y d r)

let map (d1 : 'a dim) (d2 : 'b dim) (f : 'a -> 'b) r =
{ x0 = f r.x0; y0 = f r.y0; x1 = f r.x1; y1 = f r.y1 }

module Int = struct
include Int

let ( + ) = ( + )
let ( - ) = ( - )
end

module Float = struct
include Float

let ( + ) = ( +. )
let ( - ) = ( -. )
end

let int : int dim = (module Int)
let float : float dim = (module Float)

let fi = map int float Float.of_int { x0 = 0; y0 = 1; x1 = 2; y1 = 3 }
``````
1 Like

What you want is â€śmodular implicitsâ€ť (viz. Rust â€śtraitsâ€ť). Someday â€¦

if you move a type out of the `Rect` functor then you can get rid of `RectConv`?

``````type 'a rect = {
x0 : 'a;
y0 : 'a;
x1 : 'a;
y1 : 'a;
}

module type My_Dim = sig
type t
val ( + ) : t -> t -> t
val ( - ) : t -> t -> t
val min : t -> t -> t
val max : t -> t -> t
end

module Rect (D : My_Dim) = struct
type t = D.t rect
let min_x (r : t) = D.min r.x0 r.x1
let min_y (r : t) = D.min r.y0 r.y1
let width (r : t) = D.(max r.x0 r.x1 - min r.x0 r.x1)
let height (r : t) = D.(max r.y0 r.y1 - min r.y0 r.y1)
end

let map fx fy r = { x0 = fx r.x0; x1 = fx r.x1; y0 = fy r.y0; y1 = fy r.y1 }
``````

You can save a little horizontal space if you define a type alias (well not really but itâ€™s slightly neater):

``````module type My_Dim = sig
type t [@@deriving sexp]
type binop = t -> t -> t

val ( + ) : binop
val ( - ) : binop
val min : binop
val max : binop
end
``````

Another possibility:

``````type 'a t =
{
min: 'a -> 'a -> 'a;
sub: 'a -> 'a -> 'a;
x0: 'a;
x1: 'a;
y0: 'a;
y1: 'a;
}
``````

(that is, store the functions that you need on `'a` as part of the data).

Cheers,
Nicolas