Reputation: 1045
Given is a type in order to construct a binary tree:
type tree = Leaf of int | Node of int * tree * tree
Now assume we want to express via types that a binary tree contains a node with an element zero, i.e., I would like to express something of the following form:
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)
That means, whenever I have a tree of type ZeroTree, then I can be sure that the tree contains a node with an element zero, i.e., the predicate p
holds.
Is something like that expressible in OCaml?
Upvotes: 0
Views: 349
Reputation: 4909
A hybrid solution is possible but it's clunky. Here we keep a boolean around so we can cheaply convert from a zero-tree to a regular one.
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
Upvotes: 0
Reputation: 9030
Answer 1: No. What you want is beyond the scope of OCaml type system.
Answer 2: You can define zerotree
as a completely different type from tree
.
type zerotree =
| ZLeaf
| ZNodeL of int * zerotree * tree
| ZNodeR of ... (* left for the reader *)
| ZNodeI of ...
zerotree
is either ZLeaf
, a leaf with 0
; ZNodeL
, a node whose left subtree is zerotree
; ZNodeR
, a node whose right subtree is zerotree
; or ZNodeI
, a node whose int is 0
.
Answer 3: Answer 2 only works for some simple data structures and simple invariants. In the real world we often use private types to enforce the invariants by forbid arbitrary construction of values:
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
Type zerotree
is as same as tree
but its constructors are private outside of module Z
: you cannot use the constructors to create values but only deconstruct (i.e. pattern match) them outside of the module.
Construction of the value zerotree
must be done via the functions Z.leaf
and Z.node
, which check the property you have to provide.
Upvotes: 4