Introduction to Functional Programming

Russ Ross

Computer Laboratory
University of Cambridge
Lent Term 2005


Lecture 2

Value bindings

Bind values to names using val

- val pi = 3.1416;
> val pi = 3.1416 : real
- val a = pi * 2.0 * 2.0;
> val a = 12.5664 : real

New bindings mask old bindings

- val a = a / 2.0;
> val a = 6.2832 : real
- a * 3.0;
> val it = 18.8496 : real

Functions can be bound like other values

- val sqr = fn x => x * x;
> val sqr = fn : int -> int
- val area = fn r => pi * r * r;
> val area = fn : real -> real

Numeric types

int: the integers

real: the floating point numbers

The following are defined for both int and real

You must specify the type if it is otherwise ambiguous.

- fn x:real => x * x;
> val it = fn : real -> real

Function types

Functions always have one argument and one return type, and function types are specified using ->

- fn x => x + 1;
> val it = fn : int -> int
- fn x:real => x + x;
> val it = fn : real -> real

To apply a function, give it an argument

- val double = fn x => 2 * x;
> val double = fn : int -> int
- double 19;
> val it = 38 : int
- (fn a => a mod 3) 13;
> val it = 1 : int

Operators are just infix functions. To get a prefix function, use op

- op+;
> val it = fn : int * int -> int
- it (4, 3);
> val it = 7 : int
- op div (4, 2);
> val it = 2 : int

Functions

Use fun to define a function and bind it to a name in one step

- val makeeven = fn x => 2 * (x div 2);
> val makeeven = fn : int -> int
- fun makeodd x = 2 * (x div 2) + 1;
> val makeodd = fn : int -> int

Functions defined with fun can be recursive

- fun sumn n = if n = 0 then 0 else n + (sumn (n - 1));
> val sumn = fn : int -> int

A function can return another function

- val f = fn x => fn y => x + y;
> val f = fn : int -> int -> int

Curried functions (i)

Consider this function

val f = fn a => fn b => fn c => fn d => a + b + c + d

with type

int -> int -> int -> int -> int

Function types are right-associative, so we can rewrite this as

int -> (int -> (int -> (int -> int)))

Function application is left-associative, so we can use it like this

f 1 5 11 19

which is the same as

(((f 1) 5) 11) 19

Curried functions (ii)

This is called a curried function and it is a common way to write a function with multiple arguments. fun and fn give us a shortcut

fun f a b c d = a + b + c + d

This also allows partial evaluation

- fun adder a b = a + b;
> val adder = fn : int -> int -> int
- val add5 = adder 5;
> val add5 = fn : int -> int
- add5 7;
> val it = 12 : int

Compare these two versions of compose

- fun compose f g = fn x => f (g x);
> val ('a, 'b, 'c) compose =
    fn : ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b

- fun compose f g x = f (g x);
> val ('a, 'b, 'c) compose =
    fn : ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b

(this is just the built-in infix operator o)

Lexical closures

Any name not defined as a formal parameter to a function is called a free variable and is resolved from an enclosing scope

- val pi = 3.1416;
> val pi = 3.1416 : real
- fun area r = pi * r * r;
> val area = fn : real -> real

- (fn x => fn y => x + y) 1 5;
> val it = 6 : int

When a function is defined, it captures the values that are defined at the time the function is defined. The resulting function together with its captured values is called a lexical closure.

- val pi = 4.99;
> val pi = 4.99 : real
- area 1.0;
> val it = 3.1416 : real

Characters and strings

Type char

Type string

Converting between char and string

Booleans and unit

Type bool

Type unit

Basis library

Standard ML defines a library of useful functions, collected into structures. We won't discuss structures in this course, but you can use functions in a structure using Structure.function

The basis library is automatically loaded in SML/NJ. You may need to load it explicitly with Moscow ML.

- load "Math";
> val it = () : unit
- fun g u = (Math.sin u)/u;
> val g = fn : real -> real

To load your own definitions

- use "myfile.sml";

Basis library functions are listed here:

http://www.standardml.org/Basis/overview.html

Pairs and other tuples

Tuples allow convenient grouping

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

- (2, 3.0, #"X");
> val it = (2, 3.0, #"X") : int * real * char

Tuples are another way to pass multiple values to a function

- fun max (a, b) = if a > b then a else b;
> val max = fn : int * int -> int

Built-in binary operators (those with two arguments) take pairs

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

Tuples also offer an easy way to return multiple values from a function

- fun split a = (a div 2, (a + 1) div 2);
> val split = fn : int -> int * int