Build Compound Types

Three types:

  • Each of: Tuple
  • One of: Option
  • Self reference: List

    Records

val x = {name="keke", id=2002}

Tuples as Syntactic Sugar

(* val x = (true,"hi",3) : bool * string * int *)
val x = {1=true, 3=3, 2="hi"};

Data Type

datatype myType = Twoints of int * int
                | Str of string
                | Pizza

Twoints is called a constructor of type (int * int) -> myType.

Then any myType is constructed using one of the constructors.

A value contains:

  • A tag for which constructor (e.g., Twoints).
  • Corresponding data (e.g., (4, 7)).

Case

fun f x = 
    case x of 
          Pizza => 3
        | Str s => String.size s
        | Twoints(i1, i2) => i1 + i2

In the example here, Pizza, Str s, and Twoints(i1, i2) are patterns and the RHS are expressions.

Type Synonyms

Use the keyword type

Polymorphic Datatypes

Syntax:

datatype 'a option = NONE | SOME of 'a
datatype ('a, 'b) tree = 
      Node of 'a * ('a, 'b) tree * ('a, 'b) tree
    | Leaf of 'b

Each of Pattern Matching

We can think of writing val v = e as a type of case expression. A function argument can also be a pattern, i.e., fun f p = e.

Here is an example:

(* A poor style of writing pattern matching *)
fun sum_triple tri = 
    case tri of 
        (x, y, z) => x + y + z

(* A good way of writing pattern matching *)
fun sum_triple tri = 
    let val (x, y, z) = tri
    in 
        x + y + z
    end

(* Final version *)
fun sum_triple (x, y, z) = 
    x + y + z

Interestingly, in the last example, it seems that it takes three arguments. Actually, all functions in ML take and return exactly one argument.

Function Patterns

We can also repeat the fun type:

fun f p1 = e1
    | p2 = e2
    | p3 = e3

Type Inference

ML can infer some of the type in functions.

(* ML can infer it is int * 'a * int -> int *)
fun partial_sum (x, y, z) = 
    x + z

Equality Types

''a is an equality type that can only be replaced by types that can use = (e.g., string, int).

Here is an example:

(* ''a * ''a -> string *)
fun same_thing (x, y) = 
    if x = y then "yes" else "no"

Exceptions

Type of all exceptions: exn.

(* Create an exception *)
exception MyFirstException
exception MySecException of int * int

raise MySecException (3, 4)

(* e1 handle ex => v2 *)
val x = hd [] handle MyFirstException => 10

Tail Recursion

ML will first pop the caller before the call and allow the callee to reuse the stack.