ML

Features

Interpreter

Read -- evaluate -- print cycle as in LISP.
User types expression, or variable definition, or function definition.
Interpreter prints out value and inferred type.
- 7+5 ;           
val it = 12 : int    Assign value to variable "it" if nothing else specified

- val x = 7+5;
val x = 12 : int
The interpreter keeps reading an expression until you type a semicolon, so you can spread it over several lines. The prompt changes from - to =
- 7
= +
= 5;
val x=12 : int
Comments are bracketed by (* ... *)

Simple types

int, real, bool, string, char
7, -0.2, true/false, "Bob", #"B".
Overloading but no automatic coercion.
- 2.0+3.5; 
val it = 5.5 : real
- 2 + 3.5;

stdIn:4.1-10.7 Error: operator and operand don't agree [literal]
  operator domain: int * int
  operand:         int * real
  in expression:
    2 + 3.5

Constructors

Lists

Lists of entities of the same type. Notated by square brackets.
- [2,3,4,5];
val it = [2,3,4,5] : int list

- [[2.0,3.2], [], [0.0]];
val it = [[2.0,3.2],[],[0.0]] : real list list
Unlike LISP the elements of a list must all be of the same type.
- [2.2,5];
stdIn:8.4-9.8 Error: operator and operand don't agree [literal]
  operator domain: real * real list
  operand:         real * int list
  in expression:
2.2 :: 5 :: nil

- [1,[2,4]];
stdIn:1.1-4.2 Error: operator and operand don't agree [literal]
  operator domain: int * int list
  operand:         int * int list list
  in expression:
    1 :: (2 :: 4 :: nil) :: nil
Basic list operations:
 
- 2::[3,5]; (* Corresponds to LISP CONS)
val it = [2,3,5] : int list

- hd([2,3,5]));  (* Corresponds to LISP car) 
val it = 2 : int;

- tl([2,3,4]);  (* Corresponds to LISP cdr *)
val it = [3,4] : int list
 
- [2,3]@[5,7];       (* Appending two lists *)
val it = [2,3,5,7] : int list

- [];
val it = [] : 'a list  (* 'a is a type variable. More below).


Tuples

Fixed length, heterogeneous elements. Bracketed by parentheses.
- val x = (2, 7.5, "Bob");
val x = (2,7.5,"Bob") : int * real * string

#2 x; 
val it = 7.5 : real

Records

Tuples with named fields. Bracketed by curly brackets.

- val bob = { age=32, firstName="Robert", lastName="Jones", weight=183.2};
val bob = {age=32,firstName="Robert",lastName="Jones",weight=183.2}
  : {age:int, firstName:string, lastName:string, weight:real}

- #age bob;
val it = 32 : int

Conditional

- if (1 < 2) then 3 else 4; -->
val it = 3 : int

- if (1 < 2) then 3 else 4.0; -->
stdIn:38.26-39.26 Error: types of if branches do not agree [literal]
  then branch: int
  else branch: real
  in expression:
    if 1 < 2 then 3 else 4.0 -->
-

Functions

- fun add3 I = I+3;
val add3 = fn : int -> int
Note the type: add3 is a function that maps an integer to an integer.
- add3 7;
val it = 10 : int

- add3 (7);
val it = 10 : int
Functions of multiple arguments are actually functions of the corresponding tuple.
- fun times(X,Y) = X*Y;
val times = fn : int * int -> int

- times(2,3);
val it = 6 : int

- val t = (2,3);
val t = (2,3) : int * int

- times t;
val it = 6 : int

Recursion

Iteration is carried out using recursion, as in Scheme.
- fun sumTo(N) = if (N=0) then 0 else N+sumTo(N-1); 
val sumTo = fn : int -> int

- sumTo 10;
val it = 55 : int

fun member(X,L) = 
    if (L=[]) then false 
      else if hd(L)=X then true 
      else member(X,tl(L));

stdIn:49.24 Warning: calling polyEqual
stdIn:49.54 Warning: calling polyEqual
val member = fn : ''a * ''a list -> bool

- member(5, [1,5,3]);
val it = true : bool

- fun nth(I,L) = if (I=1) then hd(L) else nth(I-1,tl(L));
val nth = fn : int * 'a list -> 'a

- nth (3, ["When", "in", "the", "course"]);
val it = "the" : string
Mutually recursive functions have to be defined together:
fun f(X) = if X <= 2 then 1 else g(X-1) + g(X-2)
    and
    g(X) = if X <= 3 then 1 else f(X-1) + f(X-3);

Patterns

Similar to Perl or Prolog, though much less powerful than either of those. Matching tuples:
val (x,y) = (1,2);
val x = 1 : int
val y = 2 : int
Matching lists:
- val x::y = [1,2,3];
stdIn:1.4-1.19 Warning: binding not exhaustive
          x :: y = ...
val x = 1 : int
val y = [2,3] : int list

- val x::y::z = [1,2,3];
stdIn:1.4-6.3 Warning: binding not exhaustive
          x :: y :: z = ...
val x = 1 : int
val y = 2 : int
val z = [3] : int list

Patterns in Functions

- fun fib 1=1 | fib 2=1 | fib N = fib(N-1) + fib(N-2);
val fib = fn : int -> int
- fib 10;
val it = 55 : int

- fun doubleList [] = [] | doubleList L = 2*hd(L)::doubleList(tl(L));
val doubleList = fn : int list -> int list
- doubleList [2,3,5];
val it = [4,6,10] : int list

- fun last [h] = h | last (h::t) = last t;
stdIn:18.3-18.38 Warning: match nonexhaustive
          h :: nil => ...
          h :: t => ...

val last = fn : 'a list -> 'a
- last [2,3,5];
val it = 5 : int

Local variables

fun twoToN(N) =
  if N=0 then 1
  else let val p = twoToN(N-1) (* declaration and initialization *)
        in p+p                 (* Body of let *)
       end;                    (* end of let *)

twoToN = fn : int -> int

- twoToN 5;
val it = 32 : int
Binding is sequential:
let val J=5
    val I=J+1
    val L=[I,J]
    val Z=(J,L)     
in (J,I,L,Z)
end;

val it = (5,6,[6,5],(5,[6,5])) : int * int * int list * (int * int list)

Lexical imbedding of functions

fun reverse(L) =
let fun reverse1(L,M) = 
          if L=[] then M
          else reverse1(tl(L),hd(L)::M);
    in reverse1(L,[])
end;
val reverse = fn : ''a list -> ''a list

- reverse [3,4,5];
val it = [5,4,3] : int list
Variables are lexically scoped

Functional Programming

Passing functions as arguments

fun applyList(f,[]) = [] | 
    applyList(f,h::t) = f(h)::applyList(f,t);
val applyList = fn : ('a -> 'b) * 'a list -> 'b list


fun add3(n) = n+3;
val add3 = fn : int -> int

- applyList(add3,[2,3,5]);
val it = [5,6,8] : int list

fun sum(f,l,u) =
  if u<=l then 0
   else f(u) + sum(f,l,u-1);
val sum = fn : (int -> int) * int * int -> int

Anonymous functions (like lambda expressions in LISP):


- sum(fn x => x*x*x, 0,10);
val it = 3025 : int

- (fn x => x*x*x) (6);
val it = 216 : int

Returning functions as values

- fun adder(N) = fn X => X+N;
val adder = fn : int -> int -> int
This gives an alternative method for defining multi-variable functions (known as "Currying"):
- adder 7 5
val it = 12 : int
- val add3 = adder 3;
val add3 = fn : int -> int
- add3 7;
val it = 10 : int

Closures as in Scheme

val add3 =
  let val I=3
  in fn X => X+I
  end;
val add3 = fn : int -> int

add3 7;
val it = 13 : int;

Type inference

Types