Math OCaml中的类型级算法

Math OCaml中的类型级算法,math,types,ocaml,Math,Types,Ocaml,好的,更多类型的黑客失败: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 en

好的,更多类型的黑客失败: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是不同的类型。是否有可能使它们成为相同的类型,并且仍然将它们作为幻影类型


(可能重复:)

首先,您的代码中有一个真正的错误:它是
let to_int=function
,而不是
let to_int=function

真正的问题是您使用的是多态递归函数:对于
nat
类型的第二个参数,您使用不同的类型递归调用它。由于使用多态递归的代码的类型推断在一般情况下是不可判定的,OCaml不会尝试为您猜测它,因此您必须使用多态递归注释来明确:

let rec to_int : type n . ('a, n) nat -> int =
   ...
另一点现在不是问题,但将来可能会成为问题(这表明您仍然需要一些GADT培训):事实上,
'a s
z
是不同的类型,这对于您的函数按照您的需要工作是至关重要的。它告诉您,如果您有一个类型为
('a,z)nat
(请注意,
'a
参数在所有这些内容中都是无用的),那么它只能是
。您可以编写以下函数,它们是总计的,不会得到耗尽性警告:

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
构造函数并不重要,它们只是让类型检查器知道
Z
永远不等于
'a
。可以用
int
bool
来代替。

在更主观的评论中,我认为过度的静态验证通常不是一件好事。要学会什么时候使用这些技巧,什么时候不使用。我的猜测是,你会发现任何删除
assert(n>0)
的技术最终都会比你开始使用的简单
assert(n>0)
的简单性、灵活性和可维护性带来有争议的附加值。理解这些技术仍然很好,因为它们偶尔会有用,但是如果你真的想要静态验证的软件,我不会使用OCaml或Haskell,而是Coq。是的,这有点过分了。不过,为了满足我的需要,Coq会更过分。我更感兴趣的是轻量级替代方案,以及如何在“常规”程序中使用它们。另外,我想以一种渐进的方式学习更多关于静态验证的知识,而不必对范式进行彻底的修改。我必须感谢你花时间来了解我这一点!正如我所说,我已经找到了几篇哈斯克尔的文章,但无法轻松翻译它们。谢谢!函数是否也可以返回类型n。纳特?无法使其工作:
let rec of_int n:int->'n nat=函数0->Zero | n->suc(of_int(n-1))
与上述错误相同(此表达式的类型为'a s nat,但预期表达式的类型为z nat)但是类型为n的符号不能编译为返回值?您的函数类型不正确,因为在某种意义上它应该是存在的,而不是通用的。这本身就值得提出一个问题。