CS 334 ML Notes

Table Of Contents

Running ML

Type sml on Unix machines to get a read-eval-print loop:

- 3 + 5;
val it = 8 : int
- it * 2;
val it = 16 : int
- val six = 3 + 3;
val six = 6 : int

Or pipe a file into the interpreter: sml < file.ml

Defining Functions

- fun succ x = x + 1;
val succ = fn : int -> int
- succ 12;
val it = 13 : int
- 17 * (succ 3);
val it = 68 : int;

Or using lambda-notation:

- val succ = fn x => x + 1;
val succ = fn : int -> int

Recursion

All functions written using recursion and if ... then ... else (and patterns):

- fun fact n =   if n = 0 then 1 else n * fact (n-1);

if ... then ... else is an expression:

- if 3<4 then "moo" else "cow";
val it = "moo" : string

The types of branches must match in type!

- if 3<4 then "moo" else 2;
stdIn:2.1-2.25 Error: types of if branches do not agree

Local Declarations

- fun cylinderVolume diameter height =
    let val radius = diameter / 2.0;
        fun square y = y * y
    in
        3.14 * square(radius) * height
    end;

val cylinderVolume = fn : real -> real -> real

- cylinderVolume 6.0 6.0;
val it = 169.56 : real

Built-in Data Types

Overloaded Operators

+,-, etc. are defined on both int and real. Which one to use depends on operands:

- fun succ x = x + 1
val succ = fn : int -> int

- fun double x = x * 2.0
val double = fn : real -> real

- fun double x = x + x
val double = fn : int -> int

Type Declarations

Can add types when type inference does not work.

- fun double (x:real) = x + x;
val double = fn : real -> real

- fun double (x:real) : real = x + x;
val double = fn : real -> real

Compound Types

Tuples

Example: (14, "moo", true): int * string * bool

Functions can take tuple argument:

- fun power (exp,base) = 
    if exp = 0 then 1       
               else base * power(exp-1,base);
val power = fn : int * int -> int
- power(3,2);

Curried Functions (named after Haskell Curry)

Curried power function:

- fun cpower exp = 
    fn base => if exp = 0 then 1        
                          else base * cpower (exp-1) base;
val cpower = fn : int -> (int -> int)

Or:

- fun cpower exp base = 
    if exp = 0 then 1
               else base * cpower (exp-1) base;

val cpower = fn : int -> (int -> int)

Why is this useful?

- fun cpower exp base =    
    if exp = 0 then 1 
               else base * cpower (exp-1) base;
val cpower = fn : int -> (int -> int)

- val square = cpower 2;
val square = fn : int -> int
- square 3;
val it = 9 : int

Records

Like tuple, but with labeled elements.

Example: { name="Gus", salary=3.33, id=11 }:{ name:string, salary:real, id:int };

Selector operator:

- val x =    { name="Gus", salary=3.33, id=11 };
- #salary(x);
val it = 3.33 : real
- #name(x);
val it = "Gus" : string

Lists

Pattern Matching

A list is one of two things:

[1, 2, 3]   =   1::[2,3]   =   1::2::[3]   =   1::2::3::nil

We can define function with these two cases:

fun product (nil) = 1
  | product (x::xs) = x * product (xs);

Patterns on integers

fun listInts 0 = [0]
  | listInts n = n::listInts(n-1);

listInts 3  [3, 2, 1, 0];

More on patterns for other data types later…

Many Types Of Lists

1::2::nil : int list;
"wombat"::"numbat"::nil : string list;

What type of list is nil?

- nil;
val it = [] : 'a list

Polymorphic type

'a is a type variable that represents any type:

1::nil    : int list
"a"::nil  : string list

The Length Function

fun length (nil) = 0 
  | length (x::xs) = 1 + length (xs);

What is the type of length?

How about this one?

fun id x = x;

Patterns and Other Declarations

Patterns can be used in in val declarations too:

val <pattern> = <exp>;

Examples:

- val x = 3;
val x = 3 : int
- val tuple = ("moo", "cow");
val tuple = ("moo","cow") : string * string
- val (x,y) = tuple;
val x = "moo" : string
val y = "cow" : string
- val list = [1,2,3];     
val list = [1,2,3] : int list
- val first::rest = list;
val first = 1 : int
val rest = [2,3] : int list
- val (x,_) = tuple;
val x = "moo" : string
- val first::_ = list;
val first = 1 : int