OCaml 模块中的抽象类型

fil*_*ard 2 ocaml module signature

我在 OCaml 中有非常简单的签名和模块:

module type S = sig
  type t 
  val y : t
end;;
Run Code Online (Sandbox Code Playgroud)

module M2 : S = struct
  type t = int
  let x = 1
  let y = x+2
end;;
Run Code Online (Sandbox Code Playgroud)

我不能使用像

M2.y
Run Code Online (Sandbox Code Playgroud)

获得3除非我指定的模块

module M2 : S with type t = int = struct ...
Run Code Online (Sandbox Code Playgroud)

为什么会这样?已经有说法了type t = int

Eri*_*kMD 5

具体的,intM2.y确实不可用,因为满足以下两个条件:

  1. 签名中的类型y抽象S
    (没有type t = ...

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

结果,您确实获得了:

let test = M2.y ;;
(* val test : M2.t = <abstr> *)
Run Code Online (Sandbox Code Playgroud)

正如关键字所建议的<abstr>,这与抽象类型的概念有关。这个概念是由 OCaml 的类型规则强制执行的一个非常强大的特性,它阻止具有签名的模块的任何用户S检查一个这样的抽象类型的具体内容。因此,通过仔细分离 ADT 的实现和签名,此属性对于在 OCaml 中实现所谓的抽象数据类型 (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 *)
    
    Run Code Online (Sandbox Code Playgroud)

    但在实践中这不是很有趣,因为你失去了一般性。然而,一种更有趣的方法是在签名中添加一个“评估器”或“漂亮的打印机”函数,例如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 *)
    
    Run Code Online (Sandbox Code Playgroud)
  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 *)
    
    Run Code Online (Sandbox Code Playgroud)

最后,需要注意的是,除了抽象类型的特性之外,OCaml 还提供了私有类型的特性,可以将其视为模块化开发中使用的具体类型和抽象类型之间的权衡。有关此概念的更多详细信息,请参见例如章节。8 Caml ref man