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值确实不可用,因为满足以下两个条件:

  1. y的类型在签名S
    中是abstract (那里没有type t = ...

  2. 模块 M2 相对于签名 S
    不透明 (换句话说,它仅限于签名 S 通过 符号 : S

结果,您确实获得了:

let test = M2.y ;;
(* val test : M2.t = <abstr> *)

正如关键字 <abstr> 所暗示的,这与 抽象类型 的概念有关。这个概念是 OCaml 的类型规则强制执行的一个非常强大的功能,它可以防止具有签名 S 的模块的任何用户检查此类抽象类型的具体内容。因此,这个 属性 对于在 OCaml 中实现所谓的 abstract data types (ADT) 非常有用,通过仔细分离 ADT 的实现和签名。

如果缺少上述两个条件中的任何一个,类型将不再是抽象的,y的具体值将显示出来。

更准确地说:

  1. 如果类型 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 *)
    
  2. 否则,如果模块 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.

示例