OCaml 模块中的抽象类型
Abstract types in modules in OCaml
我在 OCaml 中有非常简单的签名和模块:
module type S = sig
type t
val y : t
end;;
和
module M2 : S = struct
type t = int
let x = 1
let y = x+2
end;;
我不能使用像
这样的结构
M2.y
得到 3
除非我将模块指定为
module M2 : S with type t = int = struct ...
为什么会这样?已经有声明,type t = int
M2.y
的具体int
值确实不可用,因为满足以下两个条件:
y
的类型在签名S
中是abstract
(那里没有type t = ...
)
模块 M2
相对于签名 S
不透明
(换句话说,它仅限于签名 S
通过 符号 : S
)
结果,您确实获得了:
let test = M2.y ;;
(* val test : M2.t = <abstr> *)
正如关键字 <abstr>
所暗示的,这与 抽象类型 的概念有关。这个概念是 OCaml 的类型规则强制执行的一个非常强大的功能,它可以防止具有签名 S
的模块的任何用户检查此类抽象类型的具体内容。因此,这个 属性 对于在 OCaml 中实现所谓的 abstract data types (ADT) 非常有用,通过仔细分离 ADT 的实现和签名。
如果缺少上述两个条件中的任何一个,类型将不再是抽象的,y
的具体值将显示出来。
更准确地说:
如果类型 t
被具体化,您将获得:
module type S = sig
type t = int
val y : t
end
module M2 : S = struct
type t = int
let x = 1
let y = x+2
end
let test = M2.y ;;
(* val test : M2.t = 3 *)
但在实践中这并不是很有趣,因为你失去了普遍性。然而,一种更有趣的方法是在签名中添加一个“评估器”或“漂亮打印机”功能,例如下面的值int_of_t
:
module type S = sig
type t
val y : t
val int_of_t : t -> int
end
module M2 : S = struct
type t = int
let x = 1
let y = x+2
let int_of_t x = x
end
let test = M2.(int_of_t y) ;;
(* val test : int = 3 *)
否则,如果模块 M2
透明,您将获得:
module type S = sig
type t
val y : t
end
module M2 (* :S *) = struct
type t = int
let x = 1
let y = x+2
end
let test = M2.y ;;
(* val test : int = 3 *)
最后,注意到除了抽象类型的特性之外,OCaml 还提供了 私有类型 的特性,可以将其视为具体与具体之间的权衡。以及模块化开发中使用的抽象类型。有关此概念的更多详细信息,请参阅 Chap. 8 of Caml ref man.
示例
我在 OCaml 中有非常简单的签名和模块:
module type S = sig
type t
val y : t
end;;
和
module M2 : S = struct
type t = int
let x = 1
let y = x+2
end;;
我不能使用像
这样的结构M2.y
得到 3
除非我将模块指定为
module M2 : S with type t = int = struct ...
为什么会这样?已经有声明,type t = int
M2.y
的具体int
值确实不可用,因为满足以下两个条件:
y
的类型在签名S
中是abstract (那里没有type t = ...
)模块
M2
相对于签名S
不透明 (换句话说,它仅限于签名S
通过 符号: S
)
结果,您确实获得了:
let test = M2.y ;;
(* val test : M2.t = <abstr> *)
正如关键字 <abstr>
所暗示的,这与 抽象类型 的概念有关。这个概念是 OCaml 的类型规则强制执行的一个非常强大的功能,它可以防止具有签名 S
的模块的任何用户检查此类抽象类型的具体内容。因此,这个 属性 对于在 OCaml 中实现所谓的 abstract data types (ADT) 非常有用,通过仔细分离 ADT 的实现和签名。
如果缺少上述两个条件中的任何一个,类型将不再是抽象的,y
的具体值将显示出来。
更准确地说:
如果类型
t
被具体化,您将获得:module type S = sig type t = int val y : t end module M2 : S = struct type t = int let x = 1 let y = x+2 end let test = M2.y ;; (* val test : M2.t = 3 *)
但在实践中这并不是很有趣,因为你失去了普遍性。然而,一种更有趣的方法是在签名中添加一个“评估器”或“漂亮打印机”功能,例如下面的值
int_of_t
:module type S = sig type t val y : t val int_of_t : t -> int end module M2 : S = struct type t = int let x = 1 let y = x+2 let int_of_t x = x end let test = M2.(int_of_t y) ;; (* val test : int = 3 *)
否则,如果模块
M2
透明,您将获得:module type S = sig type t val y : t end module M2 (* :S *) = struct type t = int let x = 1 let y = x+2 end let test = M2.y ;; (* val test : int = 3 *)
最后,注意到除了抽象类型的特性之外,OCaml 还提供了 私有类型 的特性,可以将其视为具体与具体之间的权衡。以及模块化开发中使用的抽象类型。有关此概念的更多详细信息,请参阅 Chap. 8 of Caml ref man.
示例