Why this functor call can not be compiled?

module type Matrix = sig 
  type data 
  type t = {
    row: int;
    col: int;
    data: data;
  }
end;;

module BaseIntMatrix = struct 
  type data = int array
  type t = {
    row: int;
    col: int;
    data: data;
  }
end;;

module BaseFloatMatrix = struct
  type data = float array 
  type t = {
    row: int;
    col: int;
    data: data;
  }
end

module type Array = sig
  type t 
  type elt 
  val fromLength: int -> t
end;;

module type CommonMatrix = sig
  type data
  type t
  val zeros: row: int -> col: int -> t
end


module MakeCommonMatrix (M: Matrix) (Array: (Array with type t = M.data)): (CommonMatrix with type data = M.data and type t = M.t)  = struct
  type t = M.t 
  type data = M.data
  let zeros ~row: (row: int) ~col: (col: int) = 
    let len = row * col in 
      ({
        row=row;
        col=col;
        data=Array.fromLength(len)
      }: t)

end;;


module IntMatrix = MakeCommonMatrix (BaseIntMatrix) (struct 
  type t = int array
  type elt = int 
  let fromLength (len: int) = 
    Base.Array.create ~len:len 0
end);;

module FloatMatrix = MakeCommonMatrix (BaseFloatMatrix) (struct
  type t = float array 
  type elt = float
  let fromLength (len: int) = Base.Array.create ~len:len 0.
end)

module MakeTransfer (M: Matrix with type data = int array) = struct
  let transfer (m: M.t) = 
    let floatM = FloatMatrix.zeros ~row: m.row ~col: m.col in
    let () = for i = 0 to m.row * m.col - 1 do 
      let v = Base.Array.get m.data i in 
      Base.Array.set floatM.data i (float_of_int v)
    done in floatM
end

module IntTransferToFloat = MakeTransfer (BaseIntMatrix);;

module IntTransferToFloat2 = MakeTransfer (IntMatrix);;

The last line MakeTranfer(IntMatrix) can not be compiled because of the error below:

Signature mismatch:
...
Type declarations do not match:
  type t = BaseIntMatrix.t
is not included in
  type t = { row : int; col : int; data : data; }
Their kinds differ.ocamllsp

Why MakeTransfer(BaseIntMatrix) can be compiled but for IntMatrix is not?

This is because of what it means for a module to be compatible with a signature that defines a structural type (such as record or sum types): if you declare a signature of the kind

module type S = sig
  type t = {x: int}
end

then for a module M to be compatible with S, it must contain a type definition of the form type t = {x: int}; it is not enough to have an equality type t = s where s is of the form type s = {x: int}.

(* typechecks *)
module M : S = struct
  type t = {x:int}
end

(* does not typecheck *)
type s = {x:int}
module N : S = struct
  type t = s
end

Cheers,
Nicolas

2 Likes

Just to complete this answer, note that you can convince the compiler to typecheck the last case by simultaneously using a type alias and a type declaration:

type s = {x:int}
module N : S = struct
  type t = s = {x:int}
end
1 Like