I am writing a compiler and would like for the representation of the ast to evolve during compilation, but I also would like to factorize the code I write as much as possible. Here are the basic data structure I am working with.
module type CustomType = sig
type t
end
module type AllTypes = sig
type exp
type ty
type exp_descr
end
module type AST = sig
type exp
type ty =
| Unknown
| Typed of exp
type exp_descr =
| Leaf
| Node of exp
end
module MakeAST (E: CustomType) = struct
type exp = E.t
type ty =
| Unknown
| Typed of exp
type exp_descr =
| Leaf
| Node of exp
end
module TypedExp (TD: AllTypes) = struct
type t =
{
ty : TD.ty;
descr : TD.exp_descr;
}
end
module rec TExp : sig type t = { ty : TAST.ty; descr : TAST.exp_descr; } end = TypedExp(TAST)
and TAST : AST with type exp = TExp.t = MakeAST(TExp)
I have factorized some code in AST
and wrote an independent module to represent expressions because I know that I am going to change the representation once everything is type-checked.
Now, I want to write a printing module for this representation:
module type ExpPrint = sig
type t
val string_of_t: t -> string
end
module type ASTPrint = sig
module AST : AST
val string_of_ty: AST.ty -> string
val string_of_exp_descr: AST.exp_descr -> string
val string_of_exp: AST.exp -> string
end
module MakeASTPrint (R: AST) (E: ExpPrint with type t = R.exp) = struct
module AST = R
let string_of_exp = E.string_of_t
let string_of_ty = function
| R.Unknown -> "Unknown"
| Typed e -> "Typed: " ^ string_of_exp e
let string_of_exp_descr = function
| R.Leaf -> "Leaf"
| Node e -> "Node: " ^ string_of_exp e
end
module TExpPrint (R: ASTPrint) (E: module type of TypedExp(R.AST)) = struct
type t = E.t
let string_of_t e = R.string_of_exp_descr e.E.descr ^ " " ^ R.string_of_ty e.ty
end
module rec TEPrint : ExpPrint with type t = TExp.t = TExpPrint(TPrint)(TExp)
and TPrint : ASTPrint with module AST = TAST = MakeASTPrint(TAST)(TEPrint)
I am trying to make sure that if my AST
changes in the future and more of the types need to be custom I will be able to reuse the printing module I wrote for TExp
, namely TExpPrint
.
Unfortunately, I receive the following error message:
File "pock.ml", line 75, characters 71-75:
Error: Signature mismatch:
Modules do not match:
sig type t = TExp.t = { ty : TAST.ty; descr : TAST.exp_descr; } end
is not included in
sig
type t =
TypedExp(TPrint.AST).t = {
ty : TPrint.AST.ty;
descr : TPrint.AST.exp_descr;
}
end
Type declarations do not match:
type t = TExp.t = { ty : TAST.ty; descr : TAST.exp_descr; }
is not included in
type t =
TypedExp(TPrint.AST).t = {
ty : TPrint.AST.ty;
descr : TPrint.AST.exp_descr;
}
File "pock.ml", line 34, characters 2-68: Expected declaration
File "pock.ml", line 41, characters 22-72: Actual declaration
I do not understand why the compiler does not realize that the two types are really the same.
One way to fix it, is to replace module type of TypedExp(R.AST)
by module type of TExp
in the definition of TExpPrint
. For that matter, I could also remove E
altogether in this definition and hardcode a dependency toward TExp
.
But, I wouldn’t be able to use TExpPrint
with another representation than AST
even if I reuse TypedExp
.
EDIT: I changed TExpPrint
, TEPrint
and TPrint
to the following definitions:
module TExpPrint (AST: AST) (R: ASTPrint with module AST = AST) (E: module type of TypedExp(AST)) = struct
type t = E.t
let string_of_t e = R.string_of_exp_descr e.E.descr ^ " " ^ R.string_of_ty e.ty
end
module rec TEPrint : ExpPrint with type t = TExp.t = TExpPrint(TAST)(TPrint)(TExp)
and TPrint : ASTPrint with module AST = TAST = MakeASTPrint(TAST)(TEPrint)
The error message now is:
File "test.ml", line 75, characters 77-81:
Error: Signature mismatch:
Modules do not match:
sig type t = TExp.t = { ty : TAST.ty; descr : TAST.exp_descr; } end
is not included in
sig
type t =
TypedExp(TAST).t = {
ty : TAST.ty;
descr : TAST.exp_descr;
}
end
Type declarations do not match:
type t = TExp.t = { ty : TAST.ty; descr : TAST.exp_descr; }
is not included in
type t =
TypedExp(TAST).t = {
ty : TAST.ty;
descr : TAST.exp_descr;
}
File "test.ml", line 34, characters 2-68: Expected declaration
File "test.ml", line 41, characters 22-72: Actual declaration
Is it an OCaml compiler bug or what am I missing?