好的,更多类型的hackery失败。 :):P
在我为期一周的追求摆脱(运行时)assert(n > 0)
而不是静态检查它的时候,我想出了这个模块:
module Nat : sig
type z
type 'n s
type ('a, 'n) nat =
Zero : ('a, z) nat
| Succ : ('a, 'n) nat -> ('a, 'n s) nat
val add : ('a, 'n) nat -> ('a, 'n s) nat
end = struct
type z
type 'n s
type ('a, 'n) nat =
Zero : ('a, z) nat
| Succ : ('a, 'n) nat -> ('a, 'n s) nat
let add n = Succ n
(*
let rec to_int n = function
| Succ a -> 1 + (to_int a)
| Zero -> 0
*)
end
这给出了Peano数字,其中数字以自己的类型编码:
# Zero;;
- : ('a, Nat.z) Nat.nat = Zero
# Succ (Zero);;
- : ('a, Nat.z Nat.s) Nat.nat = Succ Zero
# add (Succ Zero);;
- : ('_a, Nat.z Nat.s Nat.s) Nat.nat = Succ (Succ Zero)
但是,最后一个函数to_int
将无法编译:
Error: This pattern [Zero -> 0] matches values of type ('a, z) nat
but a pattern was expected which matches values of type
('a, ex#0 s) nat
我认为这是因为z和s是不同的类型。是否有可能使它们成为相同的类型,并且仍然将它们作为幻像类型?
(可能重复:type level integers in ocaml)
答案 0 :(得分:6)
首先,您的代码中存在真正的错误:它是let to_int = function
,而不是let to_int n = function
。
真正的问题是您正在使用多态递归函数:您正在使用nat
类型的第二个参数的不同类型递归调用它。由于使用多态递归的代码类型推断在一般情况下是不可判定的,因此OCaml不会尝试为您猜测它,因此您必须明确使用多态递归注释:
let rec to_int : type n . ('a, n) nat -> int =
...
另一点现在不是问题,但可能会成为未来的一个问题(并表明您仍然需要对GADT进行一些培训):'a s
和z
是截然不同的事实类型是必要到您的功能工作所需。它告诉您,如果您的值为('a, z) nat
类型(请注意'a
参数在所有这些内容中都无用),它只能 为{{1} }。你可以编写以下函数,它们是完全的,你没有得到穷举警告:
Zero
如果let is_zero : ('a, z) nat -> unit = function
| Zero -> ()
(* case Succ not considered *)
let equal : type n . ('a, n) nat * ('a, n) nat -> bool = function
| Zero, Zero -> true
| Succ n1, Succ n2 -> equal (n1, n2)
(* cases (Zero, SUcc _) or (Succ _, Zero) not considered *)
和z
类型有可能重叠(例如,如果您定义'a s
),则类型检查器无法推断这些情况是不同的,并且你必须处理我在这里省略的案例。
您当前定义的问题是类型type 'a s = z
和'a s
是通过模块接口抽象的。在模块的定义中,定义(作为不同的抽象类型)是可见的,但在模块之外,您不再知道它们是如何定义的,实际上可能是z
。因此,当您在模块外部时,您也将无法再编写这些功能。解决方案是为这些类型选择具体的定义,并通过模块接口让它们可见,这样类型检查器总是知道它们不重叠:
type 'a s = z
永远不会使用那些module Nat : sig
type z = Z
type 'a s = S of 'a
...
end ...
和Z
构造函数,这只是让类型检查器知道S
永远不等于{{}}并不重要{1}}。可以使用z
和'a s
代替。