Introduction to Functional Languages

What does functional mean? Two main senses:

tex2html_wrap_inline175 Programs consist of functions with no side effects.

tex2html_wrap_inline175 Functions are supported as ``first-class'' values.

Grand Claim: functional programs are:

tex2html_wrap_inline175 clearer;

tex2html_wrap_inline175 easier to get right;

tex2html_wrap_inline175 easier to test;

tex2html_wrap_inline175 easier to transform;

tex2html_wrap_inline175 easier to parallelize;

tex2html_wrap_inline175 easier to prove things about;

tex2html_wrap_inline175 more ``fun.''

Important examples:

tex2html_wrap_inline175 Lisp, Scheme (``strict'', dynamically typed, impure)

tex2html_wrap_inline175 Standard ML, CAML (``strict'', statically typed, impure)

tex2html_wrap_inline175 Haskell, Gofer (``lazy'', statically typed, pure)

Functional Architecture

Programs are constructed by combining functions via composition; the output of one function is passed to the input of another.

Since there are no side-effects, all interaction between program components must be by passing explicit arguments and results. There are no shared variables or common data areas.

This means each function can stand on its own. It can be tested and debugged independently of any other functions.

(Also, since it's a pain to communicate information explicitly this way, programmers have an incentive to avoid unnecessary coupling between components - a sound software engineering goal.)

What is a function?

Abstractly, a function is just a mapping from a set of possible input values (the domain) to a set of possible output values (the co-domain).

Function can be specified intensionally, by means of a rule that describes how to calculate the output for a given input, or extensionally, by means of a (perhaps infinite) table that describes what the output is for any given input.

Both views are extremely important. Intensional view allows us to program a machine; extensional view allows us to abstract away from the details of the program by focusing on the externally visible behavior.

code60

Functional languages allow us to manipulate functions as just another kind of data. This often allows us to take a much higher-level, declarative approach to programming problems.

Standard ML

Standard ML is one of the best-developed examples of a functional language.

tex2html_wrap_inline175 Name originally derived from ``Meta Language;'' developed in Edinburgh in early 1980's.

tex2html_wrap_inline175 Uses so-called ``eager'' evaluation, like LISP/Scheme; more efficient (though less powerful) than so-called ``lazy'' languages such as Haskell.

tex2html_wrap_inline175 Strongly typed at compile time, unlike LISP/Scheme.

tex2html_wrap_inline175 Not doctrinaire; supports some side-effecting features for I/O, updatable store.

tex2html_wrap_inline175 Has good compilers and interpreters, including SML of New Jersey and Moscow ML.

tex2html_wrap_inline175 Has a full formal operational semantics.

Interactive System

SML provides a interactive ``read-eval-print'' loop for incremental program development.

You type value or function definitions; they are type-checked and compiled or interpreted, and are then usable in further definitions.

In Standard ML of New Jersey:

code69

Interactive System (continued)

Note that these are declarations binding constants to identifiers; they are not assignment statements! Redefining an identifier completely replaces the old binding.

In Moscow ML, similar except for form of type error message:

code74

Notice that, unless there is a type inconsistency, the system was able to infer the types of the declared identifiers automatically. In fact, SML almost never needs you to specify a type explicitly, although you always may:

code78

Loading from Files

Of course, it's often handier to write code using an auxiliary editor. You can then compile your code using copy-and-paste, or via the use command. For example, if fred.sml contains the lines:

code83

then we can load these definitions into the read-eval-print loop as follows:

code85

Note that the contents of the file are not echoed; just the ``results.''

Functions

Functions are (usually) defined using the fun keyword:

code89

Calling (``applying'') a function is specified just by writing the function name followed by the argument; no parentheses are needed (though they are generally harmless).

All ML functions take exactly one argument; we'll see ways to get the effect of multiple arguments later.

Function Typing

Function definitions and applications must be well-typed too:

code92

Note that faulty function definitions are flagged as soon as the function is defined, rather than waiting until it is applied.

Pairs and Tuples

SML has a built-in type of pairs. You can pair together any two values. In fact, SML supports triples, quads, tex2html_wrap_inline211 , arbitrary n-tuples.

code97

Useful for passing multiple arguments to a function, or returning multiple results.

code100

In fact, the built-in binary operators like + are really just pre-defined functions that take a pair argument: a+b is a shorthand for (op +)(a,b).

Lists

SML also has a built-in data type of lists, i.e., sequences of zero or more values. You can make lists of anything, but everything in a given list must have the same type.

Lists have two interchangeable representations. The first derives from the recursive definition of what a list can be:

tex2html_wrap_inline175 A list can be empty, in which case it is represented by nil.

tex2html_wrap_inline175 A list can be formed by adding a value x onto the head of an existing list y, in which case it is represented by x::y, read ``x cons y''.

We can construct any list this way:

code114

Lists (continued)

The other way to write a list is to write the elements within square brackets, separated by commas, as illustrated in the values echoed by the interactive system. This is just a shorthand; in general,

tex2html_wrap_inline219

We can also use :: and nil as patterns in case expressions that ``deconstruct'' or analyze the contents of a list to obtain its constituent components.

code128

Recursive and Polymorphic Functions

Most useful functions on lists are recursive:

code132

SML actually supports arbitrary user-defined recursive datatypes including queues, trees, etc.; the built-in list type is just a special case.

The 'a in the type of length is a type variable: it indicates that the function can be applied to lists of any kind, as illustrated. This powerful feature is called polymorphism; it helps enable code re-use.

Higher-order Functions

Here's another function on lists:

code142

Note the similarity in form to length. We can take advantage of this similarity to abstract the recursive pattern common to these functions and write them both as instances of a single higher-order function:

code147



Andrew P. Tolmach
Thu Apr 10 18:43:42 PDT 1997