next up previous
Next: About this document Up: My Home Page

Functional Programming

Function evaluation is the basic concept for a programming paradigm that has been implemented in such functional programming languages as ML.

The language ML (``Meta Language'') was originally introduced in the 1970's as part of a theorem proving system, and was intended for describing and implementing proof strategies. Standard ML of New Jersey (SML) is an implementation of ML.

The basic mode of computation in ML, as in other functional languages, is the use of the definition and application of functions.

The basic cycle of ML activity has three parts:

Here is a simple example (in SML):

- 3;
val it = 3 : int

The first line contains the SML prompt, followed by an expression typed in by the user and ended by a semicolon.

The second line is SML's response, indicating the value of the input expression and its type.

Interacting with SML

SML has a number of built-in operators and data types. For instance, it provides the standard arithmetic operators.

- 3+2;
val it = 5 : int
- sqrt(2.0);
val it = 1.41421356237309 : real

The Boolean values true and false are available, as are logical operators such as not (negation), andalso (conjunction), and orelse (disjunction).

- not(true);
val it = false : bool
- true andalso false;
val it = false : bool

SML is a strongly typed language in that all (well-formed) expressions have a type that can be determined by examining the expression.

As part of the evaluation process, SML determines the type of the output value.

Binding Names to Values

In SML one can associate identifiers with values,

- val three = 3;
val three = 3 : int
and thereby establish a new value binding,
- three;
val it = 3 : int
More complex expressions can also be used to bind values to names,
- val five = 3+2;
val five = 5 : int
Names can then be used in other expressions,
- three + five;
val it = 8 : int

Defining Functions in SML

The general form of a function definition in SML is

fun tex2html_wrap_inline380 identifier tex2html_wrap_inline382 ( tex2html_wrap_inline384 parameters tex2html_wrap_inline386 ) = tex2html_wrap_inline388 expression tex2html_wrap_inline390 ;

For example,

- fun double(x) = 2*x;
val double = fn : int -> int
declares double as a function from integers to integers.
- double(222);
val it = 444 : int
If we apply it to an argument of the wrong type, we get an error message:
- double(2.0);
Error: operator and operand don't agree

The user may also explicitly specify types:

- fun max(x:int,y:int,z:int) =
= if ((x>y) andalso (x>z)) then x
= else (if (y>z) then y else z);
val max = fn : int * int * int -> int
- max(3,2,2);
val it = 3 : int

Recursive Definitions

The use of recursive definitions is a main characteristic of functional programming languages.

These languages strongly encourage the use of recursion as a structuring mechanism in preference to iterative constructs such as while-loops.

Example.

- fun factorial(x) = if x=0 then 1
= else x*factorial(x-1);
val factorial = fn : int -> int
The definition is used by SML to evaluate applications of the function to specific arguments.
- factorial(5);
val it = 120 : int
- factorial(10);
val it = 3628800 : int

Lists

Another built-in data structure in SML are lists.

A list in SML is essentially a finite sequence of objects, all of the same type:

- [1,2,3];
val it = [1,2,3] : int list
- [true,false, true];
val it = [true,false,true] : bool list
- [[1,2,3],[4,5],[6]];
val it = [[1,2,3],[4,5],[6]] : int list list
The last example is a list of lists of integers, in SML notation int list list.

All objects in a list must be of the same type:

- [1,[2]];
Error: operator and operand don't agree

The empty list is denoted by the following symbol:

- [];
val it = [] : 'a list
Note that the type is described in terms of a type variable 'a, as a list of objects of type 'a. Instantiating the type variable, by types such as int, results in (different) empty lists of corresponding types.

Operations on Lists

SML provides some predefined functions for manipulating lists.

The function hd returns the first element of its argument list.

- hd[1,2,3];
val it = 1 : int
- hd[[1,2],[3]];
val it = [1,2] : int list
Applying this function to the empty list will result in an error.

The function tl removes the first element of its argument lists, and returns the remaining list.

- tl[1,2,3];
val it = [2,3] : int list
- tl[[1,2],[3]];
val it = [[3]] : int list list
The application of this function to the empty list will also result in an error.

The types of the two functions are as follows:

- hd;
val it = fn : 'a list -> 'a
- tl;
val it = fn : 'a list -> 'a list

More List Operations

Lists can be constructed by the (binary) function :: that adds its first argument to the front of the second argument.

- 5::[];
val it = [5] : int list
- 1::[2,3];
val it = [1,2,3] : int list
- [1,2]::[[3],[4,5,6,7]];
val it = [[1,2],[3],[4,5,6,7]] : int list list
Again, the arguments must be of the right type:
- [1]::[2,3];
Error: operator and operand don't agree

List can also be compared for equality:

- [1,2,3]=[1,2,3];
val it = true : bool
- [1,2]=[2,1];
val it = false : bool
- tl[1] = [];
val it = true : bool

Defining List Functions

Recursion is particularly useful for defining list processing functions.

For example, consider the problem of defining an SML function, call it concat, that takes as arguments two lists of the same type and returns the concatenated list.

For example, the following applications of the function concat should yield the indicated responses.

- concat([1,2],[3]);
val it = [1,2,3] : int list
- concat([],[1,2]);
val it = [1,2] : int list
- concat([1,2],[]);
val it = [1,2] : int list
In defining such list processing functions, it is helpful to keep in mind that a list is either
  • the empty list [] or
  • of the form x::y.
For example,
- [1,2,3]=1::[2,3];
val it = true : bool

Concatenation of Lists

In designing a function for concatenating two lists x and y we thus distinguish two cases, depending on the form of x:

  • If x is an empty list, then concatenating x with y yields just y.
  • If x is of the form x1::x2, then concatenating x with y is a list of the form x1::z, where z is the results of concatenating x2 with y. In fact we can even be more specific by observing that x = hd(x)::tl(x).

This suggests the following recursive definition.

- fun concat(x,y) = if x=[] then y
= else hd(x)::concat(tl(x),y);
val concat = fn : ''a list * ''a list -> ''a list

This seems to work (at least on some examples):

- concat([1,2],[3,4,5]);
val it = [1,2,3,4,5] : int list
- concat([],[1,2]);
val it = [1,2] : int list
- concat([1,2],[]);
val it = [1,2] : int list
- concat([],[]);
val it = [] : ''a list




next up previous
Next: About this document Up: My Home Page

Steve Skiena
Tue Aug 24 20:50:17 EDT 1999