Chris Reade's
Introduction
These web pages are designed for beginners learning to use Standard ML
and also as a complement to the book
Elements of Functional Programming
Very occasional reference to Java is made for comparison, although
familiarity with Java is not essential for reading these pages.
HOT Languages
An important feature of recent modern programming languages (like ML and
Java) is that they
are HOT. That is, they are Higher
Order and Typed. Higher order means
that the building blocks of programs can be treated as data to be
manipulated by programs, so programs can be built by programs rather than
from scratch every time. In the case of OO languages like Java, this means
objects can contain methods which manipulate objects; In the case of ML,
functions can pass around and use functions. The flexibility and power
this gives for programming is enormous BUT it is also dangerous because
small slips in design can wreak havoc if not pinpointed early. Strong
type systems which prevent all programs with type errors from being
executed are a must when working at this level of abstraction. Ideally,
static type systems are needed (these do all the necessary type checking at compile
time and catch errors much earlier).
Problems with Procedural Programming
Programming languages developed as an abstraction from machine
control languages. Most programming languages in use today are
procedural languages which still reflect these origins. In
particular, they still have an explicit flow of control (sequences and
loops of basic effects) and an explicit interaction with memory
(assignment statements).
There are two major problems with programs written in these languages: firstly,
they have a bottleneck (known as the von-Neumann bottleneck) caused by
continuous transfering of data to and from memory with assignments;
and, secondly they can become very unstructured and hard to maintain
or adapt for reuse when larger programs are written.
Structured
programming and later Object-oriented programming were organisational
techniques used
to alleviate the latter problem by giving better structure to
procedural programs. In particular, localising state (variables) as much
as possible, and grouping procedures (methods) with data was seen as a
route to provide for simpler maintenence of programs.
Pure functional programming goes a step further by removing state
altogether, effectively automating the
control flow and memory management. This is done by using a language based
on expression evaluation (rather than statement execution) so that the
compiler takes on the task of organising the details of the computation.
The programmer can then concentrate on what is to be put together to do
calculations, and not the details of how the book keeping and storing of
intermediate values is to be done.
It is perhaps surprising that everything that can be done with
conventional (sequential) programming languages can also be done with pure
expression evaluation (even interactive programming) and there does not
need to be any loss in performance. What is gained, is that programs can
be developed much faster (and with much more likelihood of correct
behaviour) by simplifying the programmer's task.
In fact ML is not a pure functional language in that it is possible to
write procedural programs (with assignments and side-effects) in ML.
However, a pure functional style is encouraged by the language and we will
focus on this style. [In practice, we use some simple control to do I/O
procedurally because the pure approach to interaction uses techniques
which are too advanced for this introductory module].
A crude summary would be to say that functional programming involves
designing functions which are similar to procedures or methods but without
the use of (assignable) variables.
A simpler model of computation
If you try to think about the relationship between (1) the behaviour of an
executing program and (2) the source text of the program, you need to have
a model of how the program is being executed. A big advantage of the
purely functional approach is that there is a much simpler model. The
model is just one of calculating expressions (similar to arithmetic
calculations done in school algebra) and does not require a model of
memory, variable assignment or effects over time.
[These more abstract models of computation which do not rely on an underlying
machine model were known about in the 1930s before modern computers
were developed. They strongly influenced the design of functional
programming languages which declare or describe rather than
instruct.]
Here are some pictures illustrating very simple
functional and procedural programs as
flow graphs.
The ML Workshop is a series of small exercises and examples designed to
help beginners understand the basic concepts of functional programming
and Standard ML and to be used as a supplement to reading a text book
such as
Elements of Functional Programming
by Chris Reade
published by Addison Wesley, 1989 (Referred to as [Reade89] from now on)
Although ML is highly suitable for building large systems (with a very
advanced module system), this aspect of ML will not be covered in this
workshop. Examples are kept small to illustrate ideas and to build highly
reusable tools for subsequent programming, prototyping and experimenting.
Although the examples are small and
may appear trivial in nature to begin with, you should soon see how
quite sophisticated tools can be built relatively quickly.
Much of the material in these exercises is discussed in
the above book but note that the book predates the
1997 ML standard and library (hence these supplementary web pages).
When doing any exercise it is recommended that you keep records, plan and
take time to reflect on and analyse work.
You should pay particular attention
to the following responses when you enter expressions and definitions
in an interactive ML session:
-
Error Messages (Syntax or otherwise)
-
Type information and presentation of values
Keep a record
(e.g of responses you don't understand, errors, unexpected
behaviour...) and discuss
with others any problems or things you find interesting/unexpected.
Try to predict
responses as you become familiar with ML.
There are several implementations of ML available and these work on
various platforms.
It would be nice to say these all implement exactly the
same language [Which has a fully formally specified definition],
but this is not quite true. However, it is nearly true, and Standard ML is
a remarkably stable language.
Prior to 1997, there was no standardised library (apart from a
very small collection of commonly used functions and types), so
implementations varied widely in the non-standard additions they provided.
In 1997 a standard library was designed, accompanied
by a (very small) modification to the definition of the language. The
changes to the language are almost unnoticable and most programs written
for the earlier standard (SML '90) will
easily work the the 1997 version of standard ML (SML '97).
[The most noticable change is probably the introduction of a new
character type. In SML '90, characters were regarded as
strings of size 1. In SML '97, a separate type
of characters (distinct from strings) is used.]
Running ML
Although ML is compiled, implementations give the appearance of an
interpreted language because there is an interactive front end for
ML. That is, unlike Java or C or Pascal, you can begin a dialog with
the ML system then enter definitions and expressions for evaluation
interactively. Each definition and expression is compiled as you
enter it then it is evaluated and the result is returned at the
prompt. [The ML system keeps track of compiled code for you so you only
need to worry about source code].
This applies to all implementations of the standard.
In the rest of this text, we illustrate the ML responses
using SML of New Jersey.
A Quick trial (SML of New Jersey)
The top level prompt asking you to enter a definition or expression
is:
-
Expressions can run over several lines, and should be terminated with
a ';' (followed by <return> ). The
prompt:
=
indicates that the system is waiting for completion of an
expression. If this was unexpected, you may have forgotten the ';'
or a closing ' " ' or a closing ')'.
Try entering 3+5; <return> and you should see
- 3+5;
val it = 8 : int (this line is ML's response)
-
Then try entering 3 <return> +5; <return> to get
- 3
= +5;
val it = 8 : int
-
Now try entering the following definitions
val yearMins = 365*24*60;
val yearSecs = yearMins*60;
(65-16) * yearSecs ;
Copy and Paste and Loading Files
When you type definitions and expressions directly to the ML console,
they are compiled straight away and are available for immediate
use.
However, you will want to keep non-trivial definitions, you should prepare them in
a file with a text editor. You can then load them
during the interactive session (either by copy and paste into
an ml session window or using use - see below). This allows you to
experiment and build up your own working environment within the
language. When you terminate a session your compiled definitions
will be forgotten. There is no need for you to keep compiled code as quite large
programs will compile in seconds.
During an ML session, you can type in an expression
for evaluation with the form
use "filename";
The definitions in the file will be read in, compiled and evaluated
as though typed at the prompt. The filename should be a path relative to
where you started ML from (usually your home directory).
So if 'myfile' is in a directory 'myML' in your home directory,
load it with use "myML/myfile";
Print Depth
SML of New Jersey (v110) only prints a small part of large data values and then prints "...". To change
the low limits set as default use
printdepth.sml.
Related Reading
-
The Gentle Introduction to ML
by Andrew Cummings contains different examples and some interesting diversions.
-
ML Home page
has several pointers to follow up.
- You might like to read
Luca Cardelli's paper on Type Systems
The first 9 pages will help give you a good understanding
of type system concepts (relevant to ML and Java).
© Chris Reade 1999.
Contents Page
|