第 12 章 语言扩展
5 一级模块
(在 OCaml 3.12 中引入;模式语法和包类型推断在 4.00 中引入;包类型的结构化比较在 4.02 中引入;从 4.05 开始需要更少的括号)
模块通常被认为是静态组件。此扩展使得将模块打包为一级值成为可能,该值可以在以后动态解包为模块。
表达式 ( module 模块表达式 : 包类型 ) 将由模块表达式 模块表达式 表示的模块(结构或函子)转换为封装此模块的核心语言值。此核心语言值的类型为 ( module 包类型 )。如果可以从上下文中推断出 包类型 注释,则可以省略它。
相反,模块表达式 ( val 表达式 : 包类型 ) 评估核心语言表达式 表达式 为一个值,该值必须具有类型 module 包类型,并提取封装在此值中的模块。同样 包类型 可以省略,如果 表达式 的类型已知。如果模块表达式已经带有括号,例如函子的参数是,则不需要额外的括号:Map.Make(val key).
模式 ( module 模块名 : 包类型 ) 匹配类型为 包类型 的包,并将其绑定到 模块名。它不允许在顶层 let 绑定中使用。同样 包类型 可以省略,如果可以从封闭模式中推断出来。
在 ( module 包类型 ) 类型表达式和带注释形式中出现的 包类型 语法类代表模块类型的一个子集。此子集由带有名称的模块类型组成,并带有可选约束,其形式有限:只能指定非参数化的类型。
出于类型检查目的(从 OCaml 4.02 开始),包类型使用模块类型的结构化比较进行比较。
通常,模块表达式 ( val 表达式 : 包类型 ) 不能在函子的主体中使用,因为这可能会导致与应用式函子结合使用时出现不一致。从 OCaml 4.02 开始,这在两种情况下有所放宽:如果 包类型 不包含名义类型声明(即使用正确身份创建的类型),则此表达式可以在任何地方使用,即使它包含此类类型,也可以在生成式函子的主体中使用,如第 12.15 节所述。它也可以在局部模块绑定 let module 模块名 = ( val 表达式1 : 包类型 ) in 表达式2 的上下文中在任何地方使用。
基本示例
一级模块的典型用途是在运行时从签名的几种实现中进行选择。每个实现都是一个结构,我们可以将其封装为一级模块,然后存储在哈希表等数据结构中
type picture = … module type DEVICE = sig val draw : picture -> unit … end let devices : (string, (module DEVICE)) Hashtbl.t = Hashtbl.create 17 module SVG = struct … end let _ = Hashtbl.add devices "SVG" (module SVG : DEVICE) module PDF = struct … end let _ = Hashtbl.add devices "PDF" (module PDF : DEVICE)
然后我们可以根据命令行参数选择一种实现,例如
let parse_cmdline () = … module Device = (val (let device_name = parse_cmdline () in try Hashtbl.find devices device_name with Not_found -> Printf.eprintf "Unknown device %s\n" device_name; exit 2) : DEVICE)
或者,可以在函数内执行选择
let draw_using_device device_name picture = let module Device = (val (Hashtbl.find devices device_name) : DEVICE) in Device.draw picture
高级示例
使用一级模块,可以在不使用函子的情况下对某些代码进行模块实现参数化。
let sort (type s) (module Set : Set.S with type elt = s) l = Set.elements (List.fold_right Set.add l Set.empty)
val sort : (module Set.S with type elt = 's) -> 's list -> 's list = <fun>
要使用此函数,可以包装 Set.Make 函子
let make_set (type s) cmp = let module S = Set.Make(struct type t = s let compare = cmp end) in (module S : Set.S with type elt = s)
val make_set : ('s -> 's -> int) -> (module Set.S with type elt = 's) = <fun>
版权所有 © 2024 法国国家信息与自动化研究院