简体   繁体   English

有没有办法使用OCaml的类型系统来强制执行有关值的规则?

[英]Is there are a way to use OCaml's type system to enforce rules about values?

I am quite new to OCaml and have been exploring the capabilities of the type system. 我是OCaml的新手,并一直在探索类型系统的功能。 I have two questions. 我有两个问题。

  1. In this record type, is there a way to use OCaml's type system to enforce the rule that nums1 + nums2 = all_nums? 在这种记录类型中,有没有办法使用OCaml的类型系统来强制执行nums1 + nums2 = all_nums的规则?
type foo = {all_nums: int list; nums1: int list; nums2: int list;} ;;

eg if nums1 = [1] and nums2 = [2; 例如,如果nums1 = [1],nums2 = [2; 3] then all_nums must be [1; 3]然后all_nums必须是[1; 2; 2; 3]. 3]。

  1. In this record type, is there a way to enforce using the type system that no item in nums1 should also be in nums2, ie their intersection should be empty? 在这种记录类型中,有没有办法强制使用类型系统,nums1中的项目也不应该在nums2中,即它们的交集应该为空?
type bar = {nums1: int list; nums2: int list;} ;;

eg if nums1 = [1], nums2 cannot contain 1 as well. 例如,如果nums1 = [1],nums2也不能包含1。

Thank you in advance. 先感谢您。

Yes and no. 是的,不是。 A type that depends on a runtime value is called a dependent type , and OCaml doesn't support dependent types to the full extent. 依赖于运行时值的类型称为依赖类型 ,OCaml不支持完整范围的依赖类型。 It is not typical for conventional programming languages to support them since they are making programming quite cumbersome. 传统编程语言支持它们并不常见,因为它们使编程非常繁琐。 For example, theoretically, OCaml has enough dependent typing to support your case, except that you won't be able to use list or int , and you will have to use GADT representation for them. 例如,理论上,OCaml有足够的依赖类型来支持你的情况,除了你将无法使用listint ,你将不得不使用GADT表示。 And at the end of the day, it would be hardly usable. 而在一天结束时,它几乎无法使用。 Because, OCaml type system is still static, so it must check that your program is valid for all possible sets before the program execution. 因为OCaml类型系统仍然是静态的,所以在程序执行之前必须检查程序是否对所有可能的集合有效。 This will limit the set of typable programs a lot. 这将限制可打字程序的设置。

However, using abstract types coupled with phantom types, it is possible to encode arbitrarily invariants and rely on the type system to preserve it. 但是,使用与幻像类型相结合的抽象类型,可以对任意不变量进行编码,并依赖类型系统来保留它。 The trick is to define a small trusted kernel, where the invariant is enforced manually. 诀窍是定义一个小的可信内核,其中手动强制执行不变量。

Taking your first example, 举个例子,

 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

Using this sealed representation it is impossible to create outside of the module Foo a value of type Foo.t for which the invariant_satisfied is not true . 使用这种密封表示,不可能在模块Foo之外创建一个类型为Foo.t的值,其中invariant_satisfiedtrue Therefore, your Foo is the trusted kernel - the only place where you need to check that the invariant is preserved. 因此,您的Foo是受信任的内核 - 您需要检查保留不变量的唯一位置。 The type system will take care of the rest. 类型系统将负责其余部分。

You can encode much more complex invariants and be more expressive if you will use phantom types, eg, 如果您将使用幻像类型,您可以编码更复杂的不变量并且更具表现力,例如,

 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

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


Notes 笔记

Note 1. Your first example could be encoded in such a way that it is not possible to break the invariant, for example, instead of duplicating your data in all_nums , you can represent your types {nums1 : int list; nums2 : int list} 注意1.您的第一个示例可能以不可能破坏不变量的方式进行编码,例如,您可以表示类型{nums1 : int list; nums2 : int list} ,而不是在all_nums中复制数据{nums1 : int list; nums2 : int list} {nums1 : int list; nums2 : int list} and define all_nums as a function, let all_nums = List.append {nums1 : int list; nums2 : int list}并将all_nums定义为函数, let all_nums = List.append

Note 2. In fact since OCaml, like many other programming languages, is using modular arithmetics, an addition of two positive numbers can lead to a negative number, therefore our example is botched. 注意事实2.事实上,由于OCaml与许多其他编程语言一样,正在使用模块化算法,因此添加两个正数会导致负数,因此我们的示例很拙劣。 But for the sake of example, let's ignore this :) 但是为了举个例子,让我们忽略这个:)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM