我对OCaml还是很陌生,并且一直在探索类型系统的功能。我有两个问题。
type foo = {all_nums: int list; nums1: int list; nums2: int list;} ;;
例如如果nums1 = [1]和nums2 = [2; 3],则all_nums必须为[1; 2; 3]。
type bar = {nums1: int list; nums2: int list;} ;;
例如如果nums1 = [1],则nums2也不能包含1。
谢谢。
答案 0 :(得分:4)
是,不是。依赖于运行时值的类型称为a dependent type,OCaml不完全支持依赖类型。传统的编程语言并不支持它们,因为它们使编程变得非常麻烦。例如,从理论上讲,OCaml具有足够的从属类型来支持您的情况,除了您将无法使用list
或int
,而您将不得不使用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与许多其他编程语言一样,都使用模块化算术,因此两个正数的加法会导致一个负数,因此我们的示例是拙劣的。但是为了举例说明,让我们忽略它:)