module <name> =
struct
..
end;;
module type <name> =
struct (* should have been sig *)
..
end;;
答案 0 :(得分:8)
第一个声明一个模块,第二个声明一个模块类型(又称签名)。模块类型包含type
和val
声明,而模块可以包含定义(例如,let
绑定)。您可以使用签名来限制模块的类型,就像对功能一样。例如,
module type T = sig
val f : int -> int
end
module M : T = struct
let f x = x + 1
let g x = 2 * x
end
现在,我们有
# M.f 0 ;;
- : int = 1
# M.g 0 ;;
Error: Unbound value M.g
M.g
未绑定,因为它已被签名T
隐藏。
使用模块类型的另一种常用方法是作为参数并返回仿函数的值。例如,the standard library中的Map.Make
仿函数采用带有签名Map.OrderedType
的模块,并创建一个带有签名Map.S
的模块
P.S。请注意,问题中存在错误。使用
声明模块类型module type <name> = sig
...
end
答案 1 :(得分:4)
结构(书面struct … end
)是一堆定义。可以在模块中定义语言中的任何对象:核心值(let x = 2 + 2
),类型(type t = int
),模块(module Empty = struct end
),签名(module type EMPTY = sig end
)等。模块是结构的概括:结构是一个模块,因此是仿函数(将其视为一个将模块作为参数并返回一个新函数的函数模块)。模块就像核心价值,但实际上是一层:模块可以包含任何东西,而核心价值只能包含其他核心价值¹。
签名(书面sig … end
)是一堆规范(某些语言使用术语声明)。可以在模块中指定语言中的任何对象:核心值(val x : int
),类型(type t = int
),模块(module Empty : sig end
),签名(module type EMPTY = sig end
)等。模块类型概括签名:模块类型指定模块,恰好指定结构的模块类型称为签名。模块类型是模块,普通类型是核心值。
编译单元(.ml
文件)是结构。接口(.mli
文件)是签名。
所以module Foo = struct … end
定义了一个名为Foo
的模块,它恰好是一个结构。这类似于let foo = (1, "a")
,它定义了一个名为foo
的值,恰好是一对。而module type FOO = sig … end
(注意:sig
,而不是struct
)定义了一个名为FOO
的模块类型,它恰好是一个签名。这类似于type foo = int * string
,它定义了一个名为foo
的类型,恰好是产品类型。
¹ 事实上这已不再适用,因为OCaml 3.12引入了一流的模块,但它足够接近介绍性演示。 子>
答案 2 :(得分:0)
模块类型描述模块。它与.ml和.mli
之间的区别相同