1 SML Session September 24, 2007

Start the SML interpreter in a Unix shell with sml, and in Emacs with "M^x run-sml".

1.1 Types

- 1+1;
val it = 2 : int

Read: "The value (val) of the last expression (it) equals (=) 2 (2) which (:) is of type integer (int)." it refers always to the last expression:

- it * it;
val it = 4 : int

1.1.1 Basic Types

The basic types are int, real, bool, string, char:

- 2;
val it = 2 : int
- 1.2;
val it = 1.2 : real
- true;
val it = true : bool
- "string";
val it = "string" : string
- #"a";
val it = #"a" : char

1.1.2 List Types

- [1,2,3];
val it = [1,2,3] : int list

Read: "The value of the last expression [1,2,3] is a list (list) of integers (int)."

1.1.3 Vector Types

Synonymous names for vector type: product or tuple type

- (1,1.2);
val it = (1,1.2) : int * real

Read: "The value of the last expression equals (1,1.2) which has type integer (int) in the first component and real (real) in the second component." The * says that the whole complex type is a vector type.

1.1.4 Vector vs. List Type

Already any idea why we have vector and list types? Aren't they both just collections of items?

- (1,1.2);
val it = (1,1.2) : int * real
- [1,1.2];
stdIn:9.1-9.8 Error: operator and operand don't agree [literal]
operator domain: int * int list
operand: int * real list
in expression:
1 :: 1.2 :: nil

All elements of a list must have the same type! (But there are more differences as you will see later)

1.1.5 Function Type

What is the type of the unary minus ~ and the unary boolean function not?

- ~;
val it = fn : int -> int
- not;
val it = fn : bool -> bool

Read: "The value of not is a function (fn) that (:) maps booleans to booleans (bool -> bool)." The -> says that the whole complex type is a function type.

1.1.6 Overloaded Functions

We are used to apply the unary minus not just to integers, but also to reals:

- ~1;
val it = ~1 : int
- ~1.3;
val it = ~1.3 : real

Thus ~ is applicable to values of different types: ~ is a overloaded function! There are several other overloaded functions: +, *, div, >,

1.1.7 Infix Operator vs. Prefix Function Name

What is the type of the binary function + ?

- +;
stdIn:13.1 Error: expression or pattern begins with infix identifier "+"

Oops! + is an infix operator and that is a problem here! Fortunately each infix operator has an function name in prefix notation: If * is the infix operator then op* is its function name in prefix notation.

- 1+2;
val it = 3 : int
- op+(1,2);
val it = 3 : int
- op<(1,2);
val it = true : bool

1.1.8 Mixed Complex Types

Actually op+ is a mixed complex type:

- op+;
val it = fn : int * int -> int

Read: "op+ is a function (fn) that maps pairs of integers (int * int) to (->) integers (int)."

Note the precedence of type information given by SML! The product type constructor * binds stronger then function type constructor ->. Hence int * int -> int means without omitting braces ((int * int) -> int)!

You can combine the given types to arbitrary complex types:

- (true,[1,2]);
val it = (true,[1,2]) : bool * int list
- (1,[op+,op*]);
val it = (1,[fn,fn]) : int * (int * int -> int) list

Note another type precedence in SML: The list type constructor list binds stronger then the product type constructor *. Hence the last type expression means (int * (((int * int) -> int) list))

1.2 Value Declarations

Value names are called variables.

1.2.1 Binding Names to Values of Basic Types

The general declaration form: val name = value : type;

The type information is optional. SML can infer it mostly.

- val pi = 3.141;
val pi = 3.141 : real
- val c = #"c";
val c = #"c" : char
- val s = "string";
val s = "string" : string
- c;
val it = #"c" : char

1.2.2 Binding Names to Values of Product Type

Giving a whole vector a name:

- val v = (1,1.2);
val v = (1,1.2) : int * real
- v;
val it = (1,1.2) : int * real

Giving each component a name:

- val (x,y) = (1,1.3);
val x = 1 : int
val y = 1.3 : real
- x;

1.2.3 Binding Names to Types

The general declaration form: type name = complex-type;

- type vec = real * real;
type vec = real * real

We bind names to types just for convenience. They are not really "new types".

- val v = (3.1,42.1);
val v = (3.1,42.1) : real * real
- val v = (3.1,42.1):vec;
val v = (3.1,42.1) : vec

1.2.4 Defining Functions

The general declaration form: fun name (formal-param:type):type = body;

The first type determines the type of the formal parameter, and the second the type of the return value. Again type information are always optional as long as SML can infer a type.

- fun square (x) = x*x;
val square = fn : int -> int
- square(3);
val it = 9 : int

1.2.5 Examples showing type inference

from the default type int -> int of *:

- fun square (x) = x*x;
val square = fn : int -> int

from explicit type information of the formal parameter (x:real):

- fun square (x:real) = x*x;
val square = fn : real -> real

from explicit type information of the return value (x):real:

- fun square (x):real = x*x;
val square = fn : real -> real

most explicit type information:

- fun square (x:real):real = x*x;
val square = fn : real -> real

1.2.6 Static Binding

- val pi = 3.141;
val it = 3.141 : real
- fun area (r) = pi*r*r;
val area = fn : real -> real
- area(1.0);
val it = 3.141 : real
- val pi = 3.1;
val pi = 3.1 : real
- area(1.0);
val it = 3.141 : real

1.3 Higher Order Functions

A higher order function is a function that has functions as arguments or as return value.

1.3.1 Anonymous Functions

A function without name and its application:

- fn x:real => x*x;
val it = fn : real -> real
- (fn x:real => x*x) 3.4;
val it = 11.56 : real

1.3.2 Function Definition with Anonymous Functions

We can define the square function conveniently as above:

- fun square (x:real) = x*x;
val square = fn : real -> real

In fact this is only an abbreviation of a regular value declaration:

- val square = fn (x:real) => x*x;
val square = fn : real -> real

The latter declaration style reveals that functions are just values!

1.3.3 Simple Higher Order Functions

A cascading function is a function which returns a function,e.g.:

- fun cascaded_plus (x) = (fn (y) => x+y);
val cascaded_plus = fn : int -> (int -> int)
- val plus1 = cascaded_plus(1);
val plus1 = fn : int -> int
- plus1(2);
val it = 3 : int
- cascaded_plus(1)(2);
val it = 3 : int

A artesian function is a function that has tuples as arguments (e.g. op+).

Note cascaded_plus(1)(2) computes the same as the Cartesian function op+(1,2).

You can always transform Cartesian to cascaded functions.