有没有一种方法可以使用OCaml的类型系统来执行有关值的规则?

时间:2019-03-26 18:09:28

标签: types ocaml

我对OCaml还是很陌生,并且一直在探索类型系统的功能。我有两个问题。

  1. 在这种记录类型中,是否可以使用OCaml的类型系统来执行nums1 + nums2 = all_nums的规则?
type foo = {all_nums: int list; nums1: int list; nums2: int list;} ;;

例如如果nums1 = [1]和nums2 = [2; 3],则all_nums必须为[1; 2; 3]。

  1. 在这种记录类型中,是否有一种方法可以使用类型系统强制nums1中的任何项都不应该在nums2中,即它们的交集也应该为空?
type bar = {nums1: int list; nums2: int list;} ;;

例如如果nums1 = [1],则nums2也不能包含1。

谢谢。

1 个答案:

答案 0 :(得分:4)

是,不是。依赖于运行时值的类型称为a dependent type,OCaml不完全支持依赖类型。传统的编程语言并不支持它们,因为它们使编程变得非常麻烦。例如,从理论上讲,OCaml具有足够的从属类型来支持您的情况,除了您将无法使用listint,而您将不得不使用GADT表示形式。到最后,它几乎无法使用。因为,OCaml类型的系统仍然是静态的,所以它必须在程序执行之前检查您的程序对所有可能的集合都有效。这将大大限制可键入程序的集合。

但是,结合使用抽象类型和幻像类型,可以对任意不变式进行编码,并依靠类型系统来保存它。诀窍是定义一个小的可信内核,在其中手动执行不变量。

以第一个例子为例,

 module Foo : sig 
   type t 
   val create : int list -> int list -> int list -> (t,error) result
 end = struct 
   type t = {all_nums: int list; nums1: int list; nums2: int list;}
   type error = Broken_invariant
   let create all_nums nums1 nums2 = 
      if invariant_satisfied all_nums nums1 nums 2 
      then Ok {all_nums; nums1; nums2}
      else Error Broken_invariant
 end

使用这种密封表示法,不可能在模块Foo外部创建类型Foo.t的值,而invariant_satisfied不是true。因此,您的Foo是受信任的内核-唯一需要检查不变量是否保留的地方。类型系统将负责其余的工作。

如果要使用幻像类型(例如

),则可以编码更复杂的不变量,并且更具表达力
 module Number : sig 
     type 'a t 
     type nat
     type neg
     type any = (nat t, neg t) Either.t


     val zero : nat t
     val one : nat t
     val of_int : int -> any
     val padd : nat t -> nat t -> nat t 
     val gadd : 'a t -> 'b t -> any
 end = struct 
     type 'a t = int 
     type nat 
     type neg
     type any = (nat t, neg t) Either.t

     let zero = 0
     let one = 1
     let of_int x = if x < 0 then Right x else Left x
     let padd x y = x + y (* [see note 2] *)
     let gadd x y = of_int (x + y)
 end

其中Either.t类型定义为type ('a,'b) t = Left of 'a | Right of 'b


注释

注1.您的第一个示例的编码方式是不可能破坏不变量,例如,您可以表示类型{{1},而不是在all_nums中复制数据。 }并将{nums1 : int list; nums2 : int list}定义为函数all_nums

注2。实际上,由于OCaml与许多其他编程语言一样,都使用模块化算术,因此两个正数的加法会导致一个负数,因此我们的示例是拙劣的。但是为了举例说明,让我们忽略它:)