如何在 OCaml 中跨模块使用 GADT 而不引发警告?
How to use GADTs across modules in OCaml without raising warnings?
我有两个文件:gadt1.ml 和 gadt2.ml,第二个文件依赖于第一个文件。
gadt1.ml:
type never
type _ t1 = A1 : never t1 | B1 : bool t1
type _ t2 = A2 : string t2 | B2 : bool t2
let get1 : bool t1 -> bool = function B1 -> true
let get2 : bool t2 -> bool = function B2 -> true
gadt2.ml:
let get1 : bool Gadt1.t1 -> bool = function Gadt.B1 -> true
let get2 : bool Gadt1.t2 -> bool = function Gadt.B2 -> true
当我使用 ocaml 4.02.3 (ocamlbuild gadt2.native
) 进行编译时,我收到警告 8,提示函数 Gadt2.get1 未详尽无遗。我很困惑 Gadt2.get1
会发出警告,而 Gadt1.get1
和 Gadt2.get2
不会。
我的假设是空类型 never
不能等于 bool
所以 Gadt2.get1
不应该发出警告。另一方面,如果我用参数 A1
调用 Gadt2.get1
,我会得到一个类型错误(根据需要)。警告是预期行为还是错误?我错过了什么?
顺便说一下,将 -principal
添加到编译标志不会改变任何内容。
Gadt2
只能看到Gadt1
的接口,看不到它的实现。界面看起来像这样:
type never
type _ t1 = A1 : never t1 | B1 : bool t1
type _ t2 = A2 : string t2 | B2 : bool t2
val get1 : bool t1 -> bool
val get2 : bool t2 -> bool
请注意 type never
是抽象的——没有什么可以阻止给它 RHS 的实现。特别是,您可以在 gadt1.ml 中写入 type never = bool
,此时将 A1
传递给 get1
变得合理,因此 get1
需要为这种可能性做好准备。
相比之下,string
是一个非抽象类型:它有一个已知的表示,所以它不可能等于 bool
,因此 A2
永远不会传递给 get2
.
你似乎想用 never
做的是声明一个类型,它不是抽象的而是 empty,将其表示暴露为根本没有构造函数。不幸的是,OCaml 并没有很好地支持它;定义编译器可以在本地判断为空的类型的能力有点奇怪,manual 中并未真正提及。无法在模块签名中表达 "this type is empty"。
更新:我相信这已经改变了,你现在可以写:
type never = |
表明 never
确实是一个空类型,而不是抽象类型。
正如 Ben 所说,问题在于签名 type t
是抽象的而不是空的。一种替代方法是定义一个无人居住的类型:
module One = struct
type never = { impossible : 'a . 'a }
type _ t1 = A1 : never t1 | B1 : bool t1
type _ t2 = A2 : string t2 | B2 : bool t2
let get1 : bool t1 -> bool = function B1 -> true
let get2 : bool t2 -> bool = function B2 -> true
end
module Two = struct
let get1 : bool One.t1 -> bool = function One.B1 -> true
let get2 : bool One.t2 -> bool = function One.B2 -> true
end
这不会给出任何详尽警告。
我今天 运行 遇到了同样的问题,并且花了很多时间寻找解决方案。
正如 Ben 和 gsg 所说,确实是因为 type never
是抽象的。
我认为最优雅的解决方案是将其定义为私有变体。
type never = private Never
这解决了详尽警告并清楚地表明您消除了创建此类变量的可能性。
我有两个文件:gadt1.ml 和 gadt2.ml,第二个文件依赖于第一个文件。
gadt1.ml:
type never
type _ t1 = A1 : never t1 | B1 : bool t1
type _ t2 = A2 : string t2 | B2 : bool t2
let get1 : bool t1 -> bool = function B1 -> true
let get2 : bool t2 -> bool = function B2 -> true
gadt2.ml:
let get1 : bool Gadt1.t1 -> bool = function Gadt.B1 -> true
let get2 : bool Gadt1.t2 -> bool = function Gadt.B2 -> true
当我使用 ocaml 4.02.3 (ocamlbuild gadt2.native
) 进行编译时,我收到警告 8,提示函数 Gadt2.get1 未详尽无遗。我很困惑 Gadt2.get1
会发出警告,而 Gadt1.get1
和 Gadt2.get2
不会。
我的假设是空类型 never
不能等于 bool
所以 Gadt2.get1
不应该发出警告。另一方面,如果我用参数 A1
调用 Gadt2.get1
,我会得到一个类型错误(根据需要)。警告是预期行为还是错误?我错过了什么?
顺便说一下,将 -principal
添加到编译标志不会改变任何内容。
Gadt2
只能看到Gadt1
的接口,看不到它的实现。界面看起来像这样:
type never
type _ t1 = A1 : never t1 | B1 : bool t1
type _ t2 = A2 : string t2 | B2 : bool t2
val get1 : bool t1 -> bool
val get2 : bool t2 -> bool
请注意 type never
是抽象的——没有什么可以阻止给它 RHS 的实现。特别是,您可以在 gadt1.ml 中写入 type never = bool
,此时将 A1
传递给 get1
变得合理,因此 get1
需要为这种可能性做好准备。
相比之下,string
是一个非抽象类型:它有一个已知的表示,所以它不可能等于 bool
,因此 A2
永远不会传递给 get2
.
你似乎想用 never
做的是声明一个类型,它不是抽象的而是 empty,将其表示暴露为根本没有构造函数。不幸的是,OCaml 并没有很好地支持它;定义编译器可以在本地判断为空的类型的能力有点奇怪,manual 中并未真正提及。无法在模块签名中表达 "this type is empty"。
更新:我相信这已经改变了,你现在可以写:
type never = |
表明 never
确实是一个空类型,而不是抽象类型。
正如 Ben 所说,问题在于签名 type t
是抽象的而不是空的。一种替代方法是定义一个无人居住的类型:
module One = struct
type never = { impossible : 'a . 'a }
type _ t1 = A1 : never t1 | B1 : bool t1
type _ t2 = A2 : string t2 | B2 : bool t2
let get1 : bool t1 -> bool = function B1 -> true
let get2 : bool t2 -> bool = function B2 -> true
end
module Two = struct
let get1 : bool One.t1 -> bool = function One.B1 -> true
let get2 : bool One.t2 -> bool = function One.B2 -> true
end
这不会给出任何详尽警告。
我今天 运行 遇到了同样的问题,并且花了很多时间寻找解决方案。
正如 Ben 和 gsg 所说,确实是因为 type never
是抽象的。
我认为最优雅的解决方案是将其定义为私有变体。
type never = private Never
这解决了详尽警告并清楚地表明您消除了创建此类变量的可能性。