Recursive Variants

Variant types may mention their own name inside their own body. For example, here is a variant type that could be used to represent something similar to int list:

type intlist = Nil | Cons of int * intlist

let lst3 = Cons (3, Nil)  (* similar to 3::[] or [3]*)
let lst123 = Cons(1, Cons(2, lst3)) (* similar to [1;2;3] *)

let rec sum (l:intlist) : int=
  match l with
  | Nil -> 0
  | Cons(h,t) -> h + sum t

let rec length : intlist -> int = function
  | Nil -> 0
  | Cons (_,t) -> 1 + length t

let empty : intlist -> bool = function
  | Nil -> true
  | Cons _ -> false

Notice that in the definition of intlist, we define the Cons constructor to carry a value that contains an intlist. This makes the type intlist be recursive: it is defined in terms of itself.

Types may be mutually recursive if you use the and keyword:

type node = {value:int; next:mylist}
and mylist = Nil | Node of node

But any such mutual recursion must involve at least one variant or record type that the recursion "goes through". For example:

type t = u and u = t  (* Error: The type abbreviation t is cyclic *)
type t = U of u and u = T of t  (* OK *)

Record types may also be recursive, but plain old type synonyms may not be:

type node = {value:int; next:node}  (* OK *)
type t = t*t  (* Error: The type abbreviation t is cyclic *)

Although node is a legal type definition, there is no way to construct a value of that type because of the circularity involved: to construct the very first node value in existence, you would already need a value of type node to exist. Later, when we cover imperative features, we'll see a similar idea used (successfully) for linked lists.

results matching ""

    No results matching ""