模块 <name> = struct .. end 和模块类型 <name> = struct.. end 之间有什么区别?

7
module <name> =
   struct 
     .. 
   end;;


module type <name> =
    struct (* should have been sig *)
      .. 
end;;

3
在提问之前,你尝试编译你的“模块类型 Name = struct ... end”了吗? - Pascal Cuoq
我只是想举个例子。我正在处理一个大模块,意识到我不想让人们分心于实际问题。 - Zubeen Lalani
3个回答

7

第一个声明了一个模块,第二个声明了一个模块类型(也称为签名)。模块类型包含typeval声明,而模块可以包含定义(例如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签名隐藏。

另一种常见的使用模块类型的方式是将其作为函子的参数和返回值。例如,在标准库中的Map.Make函子接受具有Map.OrderedType签名的模块,并创建具有Map.S签名的模块。

P.S. 请注意,问题中存在错误。模块类型是使用声明的。

module type <name> = sig
  ...
end

6
一个结构体(写成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引入一级模块以来,这个说法已经不再正确,但对于介绍性演示来说足够接近了。


0

模块类型描述了一个模块。这就像.ml和.mli之间的区别一样。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接