Functions, Pattern Matching And Val Bindings

Posted by Beetle B. on Mon 08 August 2016

The pattern {f1=x1, ..., fn=xn} will match against a record (assuming all the types match) {f1=v1,...,fn=vn}.

Note that since all tuples are really records, all matches are against records.

fun sum_triple (triple : int * int * int) =
  case triple of
      (x, y, z) => x + y + z

Can use patterns in val bindings as well:

fun full_name (r : {first:string, middle:string, last:string}) =
  let val {first=x, middle=y, last=z}
  in
      x ^ " " ^ y ^ " " z
  end

fun sum_triple triple =
  let val (x, y, z) = triple
  in x + y + z
  end

sum_triple works due to type checking.

In general:

val p = e

where p is a pattern.

The best way, though, is:

fun full_name {first=x, middle=y, last=z} =
  x ^ " " ^ y " " ^ z

fun sum_triple (x, y, z) =
  x + y + z

In general:

fun f p = e

where p is a pattern.

Is sum_triple taking an argument of type int*int*int or 3 arguments of type int? It is the same thing! Either way you look at it, the type is int*int*int->int.

Every function in ML takes only one argument! Behind the scenes, pattern matching is going on to the tuple argument to extract the pieces.

There are also no zero argument functions. The argument they take is ().

Since functions take only one argument, you can invoke a function without parentheses if the argument is not a tuple:

fun f x =
  1 + x

f 3  (* instead of f(3) *)

As a general rule, any time you have a case statement with one branch, you have poor style. Consider the options above.

Multiple Cases in a Function Binding

In general, if you have:

fun f x =
  case x of
      p1 => e1
    | p2 => e2
    | pn => en

you can rewrite this as:

fun f p1 = e1
  | f p2 = e2
  | f pn = en

This only works if you do not need x. You may need parentheses to avoid ambiguities.

Example:

fun append ([], ys) = ys
  | append (x::xs', ys) = x :: append(xs', ys)

fun get_second (_, x) = x