Types OCaml中的约束/保护类型

Types OCaml中的约束/保护类型,types,ocaml,Types,Ocaml,给定的是用于构造二叉树的类型: type tree = Leaf of int | Node of int * tree * tree 现在假设我们想通过类型来表示二叉树包含一个元素为零的节点,也就是说,我想用以下形式来表示: let rec p x = match x with | Leaf(y) -> y = 0 | Node(y,l,r) -> y = 0 || (p l) || (p r) type zerotree =

给定的是用于构造二叉树的类型:

type tree = Leaf of int | Node of int * tree * tree
现在假设我们想通过类型来表示二叉树包含一个元素为零的节点,也就是说,我想用以下形式来表示:

let rec p x = match x with 
            | Leaf(y) -> y = 0
            | Node(y,l,r) -> y = 0 || (p l) || (p r)

type zerotree = ZeroTree of t:tree where p(t)
这意味着,每当我有一个类型为ZeroTree的树时,我就可以确保该树包含一个元素为零的节点,即谓词
p
保持不变


类似的东西在OCaml中可以表达吗?

回答1:不可以。您想要的超出了OCaml类型系统的范围

回答2:您可以将
zerotree
定义为与
tree
完全不同的类型

type zerotree = 
  | ZLeaf
  | ZNodeL of int * zerotree * tree
  | ZNodeR of ... (* left for the reader *)
  | ZNodeI of ...
zerotree
要么是
ZLeaf
,一个带有
0
的叶子
zmodel
,其左子树为
zerotree
的节点
ZNodeR
,其右子树为
zerotree
的节点;或者
ZNodeI
,一个int为
0
的节点

答案3:答案2只适用于一些简单的数据结构和简单的不变量。在现实世界中,我们经常使用私有类型通过禁止任意构造值来强制不变量:

module Z : sig
  type zerotree = private Leaf of int | Node of int * zerotree * zerotree
  val leaf : int -> zerotree
  val node : int -> zerotree -> zerotree -> zerotree
end = struct
  type zerotree = Leaf of int | Node of int * zerotree * zerotree
  let rec p = function
    | Leaf y -> y = 0
    | Node(y,l,r) -> y = 0 || p l || p r
  let check zt = if p zt then zt else assert false
  let leaf i = check (Leaf i)
  let node i l r = check (Node (i,l,r))
end

open Z

(* let z = Leaf 1    Compile error: Cannot create values of the private type *)
(* let z = leaf 1    Runtime error *)
let z = leaf 0
let () = match z with  (* you can still pattern match *)
  | Leaf 0 -> ()
  | _ -> assert false
类型
zerotree
tree
相同,但其构造函数在模块
Z
之外是私有的:您不能使用构造函数创建值,只能在模块之外解构(即模式匹配)


必须通过函数
Z.leaf
Z.node
来构造值
zerotree
,这些函数检查您必须提供的属性。

回答1:否。您想要的超出了OCaml类型系统的范围

回答2:您可以将
zerotree
定义为与
tree
完全不同的类型

type zerotree = 
  | ZLeaf
  | ZNodeL of int * zerotree * tree
  | ZNodeR of ... (* left for the reader *)
  | ZNodeI of ...
zerotree
要么是
ZLeaf
,一个带有
0
的叶子
zmodel
,其左子树为
zerotree
的节点
ZNodeR
,其右子树为
zerotree
的节点;或者
ZNodeI
,一个int为
0
的节点

答案3:答案2只适用于一些简单的数据结构和简单的不变量。在现实世界中,我们经常使用私有类型通过禁止任意构造值来强制不变量:

module Z : sig
  type zerotree = private Leaf of int | Node of int * zerotree * zerotree
  val leaf : int -> zerotree
  val node : int -> zerotree -> zerotree -> zerotree
end = struct
  type zerotree = Leaf of int | Node of int * zerotree * zerotree
  let rec p = function
    | Leaf y -> y = 0
    | Node(y,l,r) -> y = 0 || p l || p r
  let check zt = if p zt then zt else assert false
  let leaf i = check (Leaf i)
  let node i l r = check (Node (i,l,r))
end

open Z

(* let z = Leaf 1    Compile error: Cannot create values of the private type *)
(* let z = leaf 1    Runtime error *)
let z = leaf 0
let () = match z with  (* you can still pattern match *)
  | Leaf 0 -> ()
  | _ -> assert false
类型
zerotree
tree
相同,但其构造函数在模块
Z
之外是私有的:您不能使用构造函数创建值,只能在模块之外解构(即模式匹配)


必须通过函数
Z.leaf
Z.node
来构造值
zerotree
,这些函数检查您必须提供的属性。

混合解决方案是可能的,但它很笨重。在这里,我们保留了一个布尔值,这样我们就可以廉价地将零树转换为常规树

type ('left, 'right) either = Left of 'left | Right of 'right

module type Zero_tree = sig
  type t = private Leaf of int | Node of bool * int * t * t
    (* The type of any tree. The boolean indicates whether
       this is a zero-tree.
       The type is private so we can guarantee that the
       boolean is set correctly.
    *)

  type z
    (* The type of zero-trees, a subtype of t which is guaranteed to contain
       at least one zero-node.
       In the module implementation, it uses the same representation as t.
    *)

  (* Constructors *)
  val leaf : int -> t
  val node : int -> t -> t -> t

  val leaf_zero : t
  val node_zero : t -> t -> z
  val node_left : int -> z -> t -> z
  val node_right : int -> t -> z -> z
  val node2 : int -> z -> z -> z

  (* Destructors *)
  val view : z -> t
  val classify : t -> (z, t) either
  val as_zero : t -> z option
end

module Zero_tree : Zero_tree = struct
  type t = Leaf of int | Node of bool * int * t * t
  type z = t

  let leaf n = Leaf n
  let leaf_zero = Leaf 0

  let is_zero = function
    | Leaf 0
    | Node (true, _, _, _) -> true
    | _ -> false

  let node n a b =
    let iz = n = 0 || is_zero a || is_zero b in
    Node (iz, n, a, b)

  let node_zero a b = node 0 a b
  let node_left = node
  let node_right = node
  let node2 = node

  let view x = x

  let classify x =
    if is_zero x then Left x
    else Right x

  let as_zero x =
    if is_zero x then Some x
    else None
end

混合解决方案是可能的,但它很笨重。在这里,我们保留了一个布尔值,这样我们就可以廉价地将零树转换为常规树

type ('left, 'right) either = Left of 'left | Right of 'right

module type Zero_tree = sig
  type t = private Leaf of int | Node of bool * int * t * t
    (* The type of any tree. The boolean indicates whether
       this is a zero-tree.
       The type is private so we can guarantee that the
       boolean is set correctly.
    *)

  type z
    (* The type of zero-trees, a subtype of t which is guaranteed to contain
       at least one zero-node.
       In the module implementation, it uses the same representation as t.
    *)

  (* Constructors *)
  val leaf : int -> t
  val node : int -> t -> t -> t

  val leaf_zero : t
  val node_zero : t -> t -> z
  val node_left : int -> z -> t -> z
  val node_right : int -> t -> z -> z
  val node2 : int -> z -> z -> z

  (* Destructors *)
  val view : z -> t
  val classify : t -> (z, t) either
  val as_zero : t -> z option
end

module Zero_tree : Zero_tree = struct
  type t = Leaf of int | Node of bool * int * t * t
  type z = t

  let leaf n = Leaf n
  let leaf_zero = Leaf 0

  let is_zero = function
    | Leaf 0
    | Node (true, _, _, _) -> true
    | _ -> false

  let node n a b =
    let iz = n = 0 || is_zero a || is_zero b in
    Node (iz, n, a, b)

  let node_zero a b = node 0 a b
  let node_left = node
  let node_right = node
  let node2 = node

  let view x = x

  let classify x =
    if is_zero x then Left x
    else Right x

  let as_zero x =
    if is_zero x then Some x
    else None
end

是否希望树类型始终具有有效负载为0的节点?这是否类似于想要一个具有初始值设定项的对象?您想要一个树类型,使其始终具有负载为0的节点?这就像想要一个带有初始值设定项的对象吗?我喜欢答案3)。它让我想起了OOP中的工厂模式。谢谢你让我知道!我喜欢答案3)。它让我想起了OOP中的工厂模式。谢谢你让我知道!