In this section, we describe how we can define our own user defined types in Ocaml. There are two primary ways to go about this - using variants, and using records.

Variants

In Ocaml, a variant is a datatype representing a value that can be one of several possibilities. Variants in Ocaml are analogous to enumerators in other languages.

To define a variant, we use the type keyword followed by a | delimited list of possible values. We can also assign data to a variant possibility by using the of keyword, followed by the type to assign.

(* define a variant *)
type [Type] = [Value1] | [Value2] | ... | [ValueX]
 
(* define a variant Coin which can either be Heads or Tails *)
type coin = Heads | Tails
(* define a variant Color, which can be Red, Green, or Blue,
   each with an associated integer value *)
type color = Red of int | Blue of int | Green of int ;;

Variant types must begin with a lowercase letter.

Note that we can use a variant in its own definition! This can be used to create recursive data structures, which may be useful in creating LinkedLists, Trees, and more.

type tree = Node of int * tree * tree | Leaf ;;
type lst = List of int * lst | ListEnd ;;

To assign a variable to a variant type, we use the name of a variant possibility, followed by a value if that possibility has associated data.

let a = Heads ;; (* a is Heads of type coin *)
let b = Tails ;; (* b is Tails of type coin *)
 
let r = Red(3) ;; (* r is Red with value 3, of type color *)
let g = Green ;;  (* INVALID - Green has associated data *)

Variants can be used in pattern matching operations! To pattern match with a variant, we list the variant’s possibilities as patterns to match with.

(* match variable of type coin *)
let _ = match a with
    | Heads -> print_string "Heads!"
    | Tails -> print_string "Tails!" ;;
 
(* match variable of type color *)
let _ = match r with
    | Red(x) -> x
    | Green(y) -> y
    | Blue(z) -> z ;;

Example: Variants

Below, we create a variant of type tree representing a Tree datastructure, and define a function to recurse through it using inorder traversal.

type tree = Tree of int * tree * tree | Leaf ;;
 
let rec print_all t =
    match t with
       | Tree (v, left, right) ->
          let _ = print_all left in
          let _ = print_int v in
                  print_all right
       | Leaf -> ()

Records

Another way to define a new type is Ocaml is to use records, which represents a composite of other data types, where each type has an associated name. Records in Ocaml are analogous to structures in other languages.

To define a record, we use the type keyword, followed by a ; delimited list of Name: Type pairs, denoting a particular field of the record, and its type.

type <record-name> =
    { <field> : <type>;
      <field> : <type>;
      ...
    }
 
type player =
     {
        health: int;
        defense: int;
        attack: int;
     } ;;

Then, to create an instance of a type, we use braces { } containing a ; delimited list of Field = Value pairs, assigning a value to each field of the record. Then, to access fields of the instance, we can use the . operator.

let p1 =
    {
       health = 1;
       defense = 2;
       attack = 10;
    } ;; 
 
let h = p1.health ;;  (* h is 1 *)
let d = p1.defense ;; (* d is 2 *)
let a = p1.attack ;;  (* a is 10 *)

To define a record with generic types, we need to specify what generic types are to be used in the record, before it’s name.

type ('a, 'b) graph =
     {
        var1 : 'a ;
        var2 : 'b ;
     } ;;