关于 Ocaml 中模块签名的困惑

Confusion about signature of a module in Ocaml

这两个有什么区别吗?

我不知道应该将其中哪些放入我的 .mli 文件

module Comparable : sig 
    type t
    val compare : t -> t-> int
end

module type Comparable = sig
    type t
    val compare : t -> t-> int
end

real world ocaml书中,作者说接口、签名、模块类型这些词可以互换使用。

PS:我很乐意将问题标题更改为更合适的标题,有什么建议吗?

假设您的文件名为 m.mli。如果相应的 m.ml 文件中有模块 Comparable,则第一个定义是您使用的定义。第二个是你用来声明模块类型的东西(只是模块的类型,而不是模块)。在第一种情况下,有一个实际的 compare 可以称为 M.Comparable.compare。在第二种情况下,没有比较函数,只有类型声明。

无法确定哪个适合您。他们都说得通。

.mli 文件允许约束一个 .ml 文件,请参见下面的示例:

   /* foo.ml */
   let foo1 x y = x + y
   let foo  = foo1

   /* foo.mli */
   val foo : int -> int -> int

   /* main.ml */
   open Foo
   let r = foo 4 5
   /* let r = foo1 4 5 ;; */

ocamlbuild main.native 仅在使用 foo 时编译,使用 foo1.

时编译失败

现在,当你定义一个模块时,你可以在定义这个模块时隐藏一些声明:

   module Comparable : sig 
   /* the exposed interface */
   end = struct 
   /* the computation */ 
   end

或者,为模块定义类型:

   module type Comparable = sig 
   /* the exposed interface */
   end

稍后您将能够在代码中使用该类型来约束某些模块。 以上面给出的例子(!!删除.mli文件!!)

   /* foo.ml */
   let foo1 x y = x + y
   let foo  = foo1

   /* main.ml */
   module type T1 = sig 
   val foo : int -> int -> int
   end

   module F1 : T1 = Foo
   let r = Foo.foo1 4 5 
   let r = F1.foo1 4 5 /* will fail because hiden by type T1 */

您的第一个代码片段定义了一个具有特定签名的模块 ,第二个直接定义签名而不是模块。 要了解差异,您的代码示例可能会被重写为

module type COMPARABLE = sig 
   type t
   val compare : t -> t-> int
end

module Comparable: COMPARABLE
module type Comparable = sig
  type t
  val compare : t -> t-> int
end

定义模块类型。在接口(例如 .mli 文件)内部,承诺实现(.ml)包含相同的模块类型定义。

module Comparable : sig 
    type t
    val compare : t -> t-> int
end

在接口中承诺提供相同类型的模块。相当于

module Comparable : Comparable

(假设确实定义了模块类型)。它指出相应的 .ml 包含一个名为 Comparable.

子模块

你应该把两者中的哪一个放在你身上 .mli 取决于你想做出什么样的承诺。各有各的用处。

如果需要模块类型定义作为仿函数的参数,那么它们通常出现在接口中。实际上你的模块类型 Comparable 等于 Map.OrderedType,仿函数的参数类型 Map.Make.

上述子模块的一个用例是提供可以用作仿函数参数的东西。例如 .mli 可能如下所示:

type stuff = ...
val fancy : ... (* some operations on stuff *)

module Comparable : Comparable with type t=stuff

在这种形式中,它将使类型 stuff 可用作地图的键类型。

就是说,如果您的示例是完整的真实示例,那么我怀疑您需要模块类型定义,而不是子模块:子模块不会很有用;您没有任何操作来构造 t 类型的任何内容。