在 OCaml 中没有 "polluting" 模块的类型构造函数别名

Type constructor aliases without "polluting" a module in OCaml

我想为要在给定模块中重用的构造函数定义一个类型别名,而不必在定义它们的模块的路径前加上前缀。我还想通过执行 open/include 来避免 "polluting" 后一个模块,因为它会导入太多定义。

这是我想要的示例(无法编译):

module A = struct
  type t = Red | Blue
end

module B = struct
  type t = A.t
  let f = function
    | Red -> 1  (*Error: Unbound constructor Red*)
    | Blue -> 0
  let
end

在 B 中做 include Aopen A 在这里都有效(RedBlue 是有效的),但两者都会向 B 添加定义,"polluting"它。

open 会造成最少的伤害,但仍然允许犯错误,例如如果 A 定义了一个由于 open 而在 B 中意外使用的符号,我不会出现编译错误。

有没有办法避免这种情况"pollution",同时又能避免我在模块 B 中键入 A.Red/A.Blue

是的。

您可以在 B 中定义 t 时声明它等于 A.t 并重写其构造函数列表:

module B = struct
  type t = A.t = Red | Blue
  let f = function
    | Red -> 1
    | Blue -> 0
end

类型检查器将验证定义与 A 中的定义完全相同,因此即使您修改了 A 中的类型定义,您也会被提醒在 B 中进行更改(是的,您输入了两次相同的内容,但这是一个简单的愚蠢 copy/paste).

@PatJ 的回答很好。这是另一种有时有用的方法:您可以定义三个模块,一个只包含您的类型,然后是 AB。打开 AB.

中的第一个模块

使用 OCaml 4.02.x 及更高版本您可以使用 ppx_import (https://github.com/whitequark/ppx_import).

a.ml

type t = A | B

b.ml

(* This gives a similar result to PatJ's answer *)
type a_t = [%import: A.t]
let foo = function
  | A -> 0
  | B -> 1

它不如 PatJ 建议的方法明确。如果您使用的是大型类型定义,或者如果 A.t 在开发过程中经常更改,则 ppx 可能会更简单。

ppx 方法的一个缺点是您不能 [%import] 从同一个源文件中输入类型。引入的类型需要在单独的编译单元中定义。