这两者有什么区别吗?
我不知道应该在.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 :我很乐意将问题标题更改为更合适的标题,任何提案?
答案 0 :(得分:3)
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
类型的任何东西。
答案 1 :(得分:2)
您的第一个代码片段定义了具有特定签名的模块 ,第二个直接定义签名而不是模块。 要了解其中的差异,可以将代码示例重写为
module type COMPARABLE = sig
type t
val compare : t -> t-> int
end
module Comparable: COMPARABLE
答案 2 :(得分:1)
假设您的文件名为m.mli
。如果相应的m.ml
文件中包含模块Comparable
,则第一个定义是您使用的定义。第二个是用于声明模块类型(仅仅是模块的类型,而不是模块)。在第一种情况下,实际compare
可以称为M.Comparable.compare
。在第二种情况下,没有比较函数,只是类型声明。
不可能知道哪个对你来说是正确的。它们都有意义。
答案 3 :(得分:1)
.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 */