CS 334: SML FAQ

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

  • unit
    • only value is ()
  • bool
    • true, false
    • operators not, andalso, orelse
  • int
    • ..., ~2, ~1, 0, 1, 2, ...
    • +, -, *, div, mod, abs
    • =, < , <=, etc.
  • real
    • 3.17, 2.2, ...
    • +, -, *, /
    • <, <=, etc.
    • no implicit conversions from int to real: 2 + 3.3 is bad
    • no equality (test that -0.001 < x-y < 0.001, etc.)
  • string
    • "moo"
    • "moo" ^ "cow"

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

  • Examples: [1, 2, 3, 4], ["wombat", "numbat"]

  • nil is empty list (sometimes written [])

  • all elements must be same type
  • Operations

    - length [1,2,3];
    val it = 3 : int
    
    (* append: *)
    - [1,2] @ [3,4]; 
    val it = [1,2,3,4]: int list
    
    (* cons: *)
    - 1::[2,3];   
    val it = [1,2,3] : int list
    
    (* "mapcar": *)
    - map succ [1,2,3];
    val it = [2,3,4] : int list
    
  • Deconstructing list with hd and tl:

    - fun product (nums) = 
        if (nums = nil) 
            then 1 
            else (hd nums) * product(tl nums);
    
    val product = fn : int list -> int
    
    - product([5, 2, 3]); 
    val it = 30 : int;
    

    Avoid this! Use pattern matching instead.

Pattern Matching

A list is one of two things:

  • nil
  • "first elem" :: "rest of elems"
[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 tuple = ("moo", "cow");
val (x,y) = tuple;
val myList = [1, 2, 3];
val w::rest = myList;
val v::_ = myList;