将类型注释与仿函数一起使用时如何修复 OCaml 编译错误?

How to fix the OCaml compile error when using type annotations with functors?

我是 OCaml 的新手,正在尝试使用函子。当我将模块类型注释与函子一起使用时,这会导致我的代码出现编译时错误。

当我删除 : Printable(来自 module FromToString 行)和 : ToString(来自 module IntToString 行)注释时,以下程序编译无误:

module type ToString =
sig
    type t
    val to_string: t -> string
end

module type Printable =
sig
    type t
    val print: t -> unit
end

module FromToString (S:ToString) : Printable  =
struct
    type t = S.t
    let print a = print_string ( S.to_string a) 
end

module IntToString : ToString =
struct
    type t = int
    let to_string = string_of_int
end

module PrintableInt = FromToString(IntToString)
let () = PrintableInt.print 3

但是,当我添加这些注释时(如代码所示),编译器给出以下错误:

File "Functor.ml", line 26, characters 28-29:
Error: This expression has type int but an expression was expected of type
         PrintableInt.t = FromToString(IntToString).t

如何在不导致编译错误的情况下使用这些注释?

根本问题是您的签名约束使生成的模块过于不透明。当你约束你的仿函数结果时:

 module FromToString (S:ToString) : Printable = ...

您正在使类型 t 成为抽象类型,它只能由 to_string 函数使用并且永远不会生成。换句话说,Printable 类型的模块本身是不可用的。

从仿函数开始时,查看编译器为生成的模块推断出的模块类型通常很有用。 在 FromToString 的情况下,这是:

module FromToString (S:ToString) : sig
  type t = S.t
  val print: t -> unit
end = ...

可以看到结果的推断模块类型

 sig
   type t = S.t
   val print: t -> unit
 end

它与 Printable 非常相似,只是现在类型 t 等于参数模块 S 的类型 t

因此,可以重用 Printable 通过添加带有 with 约束的类型相等来编写结果的完整模块类型:

  module FromToString (S:ToString): Printable with type t = S.t = struct
    type t = S.t
    let print a = print_string ( S.to_string a) 
  end

同样的问题出现在 IntToString 上,可以用类似的方式解决:

module IntToString : ToString with type t = int =
struct
    type t = int
    let to_string = string_of_int
end

然后编译错误就没有了:

module PrintableInt = FromToString(IntToString)
let () = PrintableInt.print 3

必须使用 with 表示法来制作类型 t public:

module type ToString =
sig
    type t
    val to_string: t -> string
end

module type Printable =
sig
    type t
    val print: t -> unit
end

module FromToString (S:ToString) : Printable with type t = S.t =
struct
    type t = S.t
    let print a = print_string ( S.to_string a) 
end

module IntToString : ToString with type t =int  =
struct
    type t = int
    let to_string = string_of_int
end

module PrintableInt = FromToString(IntToString)
let () = PrintableInt.print 3