Introduction
Functional and imperative programming languages are primarily
distinguished by the control over program execution and the
data memory management.
-
A functional program computes an expression. This computation
results in a value. The order in which the operations needed
for this computation occur does not matter, nor does the physical
representation of the data manipulated, because
the result is the same anyway. In this setting,
deallocation of memory is managed implicitly by the language itself: it relies on an
automatic garbage collector or GC; see
chapter 9.
- An imperative program is a sequence of instructions modifying a
memory state. Each execution step is enforced by rigid control structures that
indicate the next instruction to be executed. Imperative
programs manipulate pointers or references to values more often
than the values themselves. Hence, the memory space needed to
store values must be allocated and reclaimed explicitly, which
sometimes leads to errors in accessing memory. Nevertheless,
nothing prevents use of a GC.
Imperative languages provide greater control over execution and the
memory representation of data. Being closer to the actual machine,
the code can be more efficient, but loses in execution safety. Functional programming, offering a higher level of abstraction,
achieves a better level of execution safety: Typing (dynamic or static) may be stricter in this case, thus
avoiding operations on incoherent values. Automatic storage
reclamation, in exchange for giving up efficiency, ensures the current
existence of the values being manipulated.
Historically, the two programming paradigms have been seen as
belonging to different universes: symbolic applications being suitable
for the former, and numerical applications being suitable for the
latter. But certain things have changed, especially techniques for
compiling functional programming languages, and the efficiency of GCs.
From another side, execution safety has become an important, sometimes the predominant criterion in the quality of an application. Also familiar is the ``selling point'' of the Java language, according to
which efficiency need not preempt assurance, especially if efficiency remains reasonably good. And this
idea is spreading among software producers.
Objective CAML belongs to this class. It combines the two programming
paradigms, thus enlarging its domain of application by allowing algorithms to be written in
either style. It retains, nevertheless, a good degree of
execution safety because of its static typing, its GC, and its exception mechanism.
Exceptions are a first explicit execution control structure; they make it possible to break out of a computation
or restart it. This trait is at the boundary of the two models,
because although it does not replace the result of a computation, it can modify the order of execution.
Introducing physically mutable data can alter the behavior of the purely functional part of the
language. For instance, the order in which the arguments to a function are evaluated can be
determined, if that evaluation causes side effects. For this reason,
such languages are called ``impure functional languages.'' One loses
in level of abstraction, because the programmer must take account of
the memory model, as well as the order of events in running the
program. This is not always negative, especially for the efficiency
of the code. On the other hand, the imperative aspects change the
type system of the language: some functional programs, correctly
typed in theory, are no longer in fact correctly typed because of the
introduction of references. However, such programs can easily be
rewritten.