0% found this document useful (0 votes)
46 views

Harish Karnick: 1 See Resonance, Vol.19, No.4, pp.345-367, 2013

Lisp functional programming

Uploaded by

Jr Ribs
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
46 views

Harish Karnick: 1 See Resonance, Vol.19, No.4, pp.345-367, 2013

Lisp functional programming

Uploaded by

Jr Ribs
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 14

GENERAL ¨ ARTICLE

LISP

Harish Karnick

This article discusses the programming language


LISP. The language was created by John Mc-
Carthy for an arti¯cial intelligence application.
LISP was the ¯rst functional language based on
a mathematical theory of computation. It has
been standardized and is still used fairly widely.
Harish Karnick teaches in The language also had a huge in°uence on later
the Department of languages. Its implementation introduced inter-
Computer Science and preted languages, source-level debugging and gar-
Engineering at IIT bage collection. These facilities are a common
Kanpur. His interests are
machine learning,
denominator for many languages today. We dis-
cognition and more cuss the mathematical foundations of LISP the
generally artificial interpreter and brie°y touch on its in°uence on
intelligence. Programming other languages and its status today.
languages, their history
and evolution is a hobby 1. Introduction
and LISP is a particular
favourite. Of languages that are still alive LISP is the second old-
est. It was created by John McCarthy in 1958 [1], one
year after John Backus created FORTRAN. The two
languages are very di®erent in conception, design, ap-
plication domain and ¯nally in how they have evolved
over time. McCarthy invented the language as a tool
for the Advice Taker program which required a way to
express and manipulate symbolic expressions [1].
LISP is fundamentally a functional language inspired by
1 See Resonance, Vol.19, No.4, the lambda calculus1 (or ¸-calculus) [2,3] which is a com-
pp.345–367, 2013. putational formalism based on functions and function
application. This is an alternative to the more widely
known computational model, the Turing machine [4,5],
Keywords which is much closer to a computer, the device that sits
LISP, EVAL, O-calculus, func-
on our desks.
tional language, McCarthy, gar-
bage collection, interpreter.

208 RESONANCE ¨March 2014


GENERAL ¨ ARTICLE

In this article we look at the basic computational model


at the heart of LISP; the language itself; and ¯nally how
LISP has evolved and in°uenced other languages.
2. ¸-Calculus: The Foundation for LISP
Consider simple addition where we are adding x and y,
which we often write as the expression x + y where x, y
are variables that are substituted with the values that
we want to add. Another way to look at this is to think
of + as a function of two arguments +(x; y); so when we
want to add, say 3 and 4 we can write +(3; 4) and when
we evaluate the function it returns the value 7, the sum
of x and y. We can easily generalize this idea and de¯ne
any function f with a known number of arguments, say
n, f (x1 ; x2 ; : : : ; xn ). Here, x1; : : : ; xn are called the for-
mal arguments and when we actually wish to evaluate
the function f we provide the `actual arguments' (like
3 and 4 for + above) and evaluate the function to get
the resulting answer. These kind of functions are part
of most programming languages in one form or another.
A more interesting observation is to view a function of
two arguments as one where if we provide only one ac-
tual argument then the function returns as an answer, a
function of a single argument with the actual argument
frozen to the value provided. For example, let us write
the + function as: +(x)(y) and let us say we evaluate
+(3); the resulting answer is a function of one argument
that adds 3 to its actual second argument. So, if we
allow functions themselves to be treated as values that
can be returned as answers then we are able to build
a computational model with functions having just one Alonzo Church
argument. formalized function
Alonzo Church formalized function construction and fun- construction and
ction application in a systematic way through the ¸- function application
calculus. For example, assuming that + is a primitive in a systematic
function we can write the above as: ¸x:¸y: + x y where way through the O-
the ¸ marks out x and y as formal arguments. calculus.

RESONANCE ¨ March 2014 209


GENERAL ¨ ARTICLE

Let us take a brief, but decidedly incomplete, look at


the ¸-calculus (for a systematic introduction see [6]).
Let V be a set of variables fx; y; z; w; : : : g and C a set
of constants fa; b; c; : : : g. We assume we have as many
variables (or constants) as we want; we can symbolize
them by using subscripts when we need new ones. Then
the following four rules de¯ne a ¸-expression (we sym-
bolize a ¸-expression by capital letters and optionally
subscripts, e.g., E1 ).

1. Any variable from V or a constant from C is a ¸-


expression.

2. If E1 , E2 are ¸-expressions then E1 E2 is a ¸-expre-


ssion called an application. This models function
application. Think of E1 as a function and E2 as
an argument.

3. If E1 is a ¸-expression and x is a variable then


¸x:E1 is a ¸-expression called an abstraction. This
models function creation with x as a formal argu-
ment.

4. If E1 is a ¸-expression then (E1) is a ¸-expression.


This is for notational convenience much as we use
brackets in algebraic expressions.

Note how the set of all ¸-expressions is de¯ned by just


four recursive rules.
Calculation in the ¸-calculus is done by doing reduc-
tions, more precisely beta reduction (or ¯-reduction). A
Calculation in the ¸-expression of the form (¸x:E1 )E2 is called a ¯-redex
O-calculus is and a ¯-reduction substitutes E2 for all `free' occur-
done by doing rences of variable x in E1 by E2 . If we assume that
reductions, more variable names never clash (remember we have as many
precisely variables as we want and we can rename variables apart)
beta reduction (or then we can ignore the word `free'. Let us look at some
E-reduction).

210 RESONANCE ¨March 2014


GENERAL ¨ ARTICLE

examples of reductions (! means that the left-hand side


¯
is ¯-reduced to the right-hand side):

Example 1.

(¸x:(¸y:x y))E1 ! ¸y:E1 y


¯

Example 2.
(¸x:y)E1 ! y
¯

Note that there is no x in the ¸-expression which can be


substituted by E1 ; so we are just left with y.
Example 3.

(¸x:((¸y:x (y y))E1))E2
!(¸y:E2 (y y))E1
¯

!E2 (E1 E1 )
¯

A ¯-reduction need not terminate as in the example be-


low.
Example 4.

(¸x:x x)(¸y:y y)
!(¸y:y y)(¸z:z z) renamed y with z
¯

!(¸z:z z)(¸w:w w) renamed z with w


¯

:::
:::

A ¸-expression that does not have a ¯-redex is said to


A O-expression
be in normal form. So, ¯-reduction ends when a ¸-
expression is in normal form. If a ¸-expression contains that does not have
more than one ¯-redex then the outermost ¯-redex is a E-redex is said to
reduced ¯rst. This is called normal order reduction. be in normal form.

RESONANCE ¨ March 2014 211


GENERAL ¨ ARTICLE

It is amazing that using the above we can calculate any


computable function whatsoever. The natural numbers
can be represented by ¸-expressions and therefore all of
arithmetic can be done.
3. LISP: The Language
LISP gives a language dimension to the ¸-calculus com-
putational model with some changes and extensions to
make it practical to use.
LISP replaces ¸-expressions with S-expressions (sym-
bolic expressions). S-expressions are de¯ned much as we
did ¸-expressions. In the orginal paper [1] McCarthy
de¯nes M-expressions (meta expressions) that contain
S-functions that act on S-expressions. There are ¯ve
primitive S-functions and two functional forms, the con-
ditional and recursion, to compose them. In addition,
through the `label' mechanism, a functional form can be
named and called recursively within its own M-expression.
Separately, he provides a way to translate M-expressions
to S-expressions and de¯nes an universal S-function ap-
ply that can evaluate the resulting S-expression to give
the answer that we would expect from the M-expression.
While this appears complicated it is really analogous to
the basic von Neumann stored program idea where both
program and data are stored in the same memory and a
higher level interpreter, the CPU, interprets everything
suitably.
We will simplify the exposition and de¯ne an interpreter
EVAL that works on S-expressions. We start by de¯n-
ing an S-expression: Assume we have an in¯nite set of
2
atomic symbols2 including numbers and strings of char-
Atomic symbols are entities
that EVAL treats as atomic (even
acters. An S-expressions is de¯ned by the following two
though they may be composed rules:
of parts), for example, numbers 1. Any atomic symbol is an S-expression.
(a concatenation of digits) and
names (a concatenation of let- 2. If S1 and S2 are S-expressions then (S1 :S2) is an
ters).
S-expression.

212 RESONANCE ¨March 2014


GENERAL ¨ ARTICLE

An S-expression like (A.(B.(C.(D.NIL)))) is often writ-


ten as a list (A; B; C; D). Here NIL is a special atomic
symbol that terminates lists. We will use mostly lists in-
stead of S-expressions since they are easier to read and
write.
We start by de¯ning ¯ve primitive functions on S-express-
ions. These are car, cdr, cons, atom, eq. The ¯rst
two are used to disassemble S-expressions, the third is
used to construct or assemble an S-expression and the
last two are predicates. Note that these are partial func-
tions and may give the unde¯ned value ?, if they get the
wrong arguments. The primitive functions car and cdr
are so named because the ¯rst interpreter for LISP im-
plemented on IBM 704 used the address register to point
to the ¯rst element of the list and the data register to
point to the rest of the list. They stand for contents
of address register and contents of data register respec-
tively. Notationally, we have written the functions in a
typewriter font for clarity but for all practical purposes
they are S-expressions. The interpreter EVAL interprets
them suitably. We will describe later exactly how EVAL
works. The primitive functions have the following mean-
ing (note that a pre¯x-based function notation is used,
so the function is the ¯rst element of the list):
The primitive
² car gives the head or ¯rst element of an S-expression
functions car and
or list.
Example: (car (A.B)) is A, (car(A,B,C)) is also A. cdr are so named
because the first
² cdr gives the second element of an S-expression or the interpreter for LISP
tail of a list. implemented on IBM
Example: (cdr (A.B)) is B, (cdr(A,B,C)) is the list 704 used the
(B,C). address register to
² (cons A B) constructs the S-expression (A.B). Simi- point to the first
larly, (cons A (B, C)) constructs the list (A, B, C) and element of the list
(cons A NIL) gives the list (A). and the data register
to point to the rest of
the list.

RESONANCE ¨ March 2014 213


GENERAL ¨ ARTICLE

One way to ² (atom S1) is true (T) if S1 is an atomic S-expression


understand cond and false (F) otherwise. For example, (atom A) is true
is as a nested if- but (atom (A.B)) or (atom (A,B)) is false.
else-statement.
² (eq S1 S2 ) is de¯ned if and only if S1 and S2 are atomic
For example:
else it is unde¯ned. It is true if they are identical atoms
if (C1 is true) then
otherwise it is false.
S1 else if (C2 is Example: (eq A B) is false, (eq A A) is true, (eq (A.B)
true) then S2 .... A) or (eq (A B) B) give ?.
else if (Cn is true)
then Sn else S. The basic functional forms in LISP are composition, the
conditional and recursion. Of these only the conditional
needs a bit of explanation.
(cond (C1 S1)(C2 S2)...(Cn Sn)(T S))
The interpreter evaluates each conditional S-expression
C1, C2, etc. in turn and for the ¯rst condition, say
Ci, that is true, it evaluates the corresponding Si and
returns the resulting value as the answer. If all the con-
ditions evaluate to false then the answer is the value
obtained on evaluating the S-expression S since its con-
dition is trivially true. If (T S) is absent then the value
is the atom NIL.
To make recursive functions easy to use there is a nam-
ing facility which binds a name to any arbitrary S-expre-
ssion. In particular this expression can be the body of
a function. So, this allows one to call a function within
itself.
Let us see some examples. All LISP code is in type-
writer font for clarity. This is distinct from the use
of the typewriter font earlier to distinguish between S-
functions and S-expressions. The naming facility uses
defun to bind function values to names. We will freely
use logical connectives like and, or, not to combine pred-
icates.

214 RESONANCE ¨March 2014


GENERAL ¨ ARTICLE

Example 5.

;;;; returns T if S1 and S2 are identical


;; S-expressions F otherwise
(defun equal (S1 S2)
(cond
((atom S1)(cond ((atom S2) (eq S1 S2))
(T NIL)))
((atom S2) NIL) ; S1 is not an atom so
;S1, S2 are not equal if S2 is an atom
(T (and (equal (car S1)(car S2))(equal
(cdr S1) (cdr S2))))
)
)

What defun does is bind the S-expression that repre-


sents the body of the function de¯nition (see below) to Lambda signals
the name equal (atomic S-expression). An S-expression that the S-
that represents the function is given below. Note that
expression is a
Lambda signals that the S-expression is a function de¯ni-
function definition.
tion. This is followed by the formal arguments which in
turn is followed by the body of the function de¯nition.

(Lambda (S1 S2)


(cond
((atom S1)(cond ((atom S2) (eq S1 S2))
(T NIL)))
((atom S2) NIL) ; S1 is not an atom so
;S1, S2 are not equal if S2 is an atom
(T (and (equal (car S1)(car S2))(equal
(cdr S1) (cdr S2))))
)
)

Below are a few more examples of LISP function de¯ni-


tions:

RESONANCE ¨ March 2014 215


GENERAL ¨ ARTICLE

Example 6.

;;;;Checks if a list is null or not


(defun null (L)
(and (atom L) (eq L NIL))
)

;;;;Finds length of list L. Uses the null


;;function above.
(defun length (L)
(cond
((null L) 0)
(T (plus 1 (length (cdr L))));plus adds
its two arguments
)
)

Example 7.

;;;;Appends two lists L1, L2.


(defun append (L1 L2)
(cond
((null L1) L2)
((null L2) L1)
(T (cons (car L1) (append (cdr L1) L2)))
)
)
;;;;Reverses list L
(defun rev (L)
(cond
((null L) L)
(T (append (reverse (cdr L)) (cons (car L)
NIL)))
)
)

;;;A much better way to reverse a list


(defun revBetter (L)

216 RESONANCE ¨March 2014


GENERAL ¨ ARTICLE

(cond
((null L) L)
(T (revfn L NIL)))
)
;;;;revfn does all the work
(defun revfn (L R)
(cond
((null L) R)
(T (revfn (cdr L) (cons (car L) R)))
)
)

The above examples show how one can build complex


functions by composing simpler functions. A typical
modern LISP system has hundreds of functions already
de¯ned as part of the LISP system.
4. EVAL: The LISP Interpreter
The LISP interpreter EVAL is a function that takes an S-
expression, evaluates it and returns the result obtained.
This is the equivalent of ¯-reduction in the ¸-calculus.
Broadly, (EVAL L) works as follows: If L is an atom
then it returns the value bound to that atom. In some
cases the atom itself is the value, e.g., when the atom
is a number or NIL (representing the empty list). If L
is a list and the ¯rst element signals that L is to be
treated specially, e.g., defun, then it evaluates it in a
special way; otherwise it evaluates each element of the
list and then applies the function associated with the
¯rst element of the list to the arguments represented by
the rest of the list. The interpreter EVAL is available to
the LISP programmer, usually as the function eval. So, The interpreter
it is possible to dynamically create a function at run time EVAL is available
and apply it to arguments by using eval. Two special to the LISP
functions quote and setq are very useful in this context. programmer,
When (quote S) is evaluated it returns its argument usually as the
literally (as is) as the answer. The function setq allows function eval.

RESONANCE ¨ March 2014 217


GENERAL ¨ ARTICLE

one to bind an S-expression to an atom. So, (setq A


10) will bind atom A (think of it as a variable) to 10.
Note that the ¯rst argument of setq is not evaluated
but treated literally. For a second example consider:
(setq B (quote (car C))) will bind the atom B to
the S-expression (car C).
The following example shows what happens when eval
is used explicitly:

(setq L (quote (length L1)))


;length is the function defined in example 6
(setq L1 (quote (1,2,3,4)))
(eval L)

The above will evaluate to 4 which is the length of list


L1. Note how we have constructed the list L at run
time which will be bound to the S-expression (length
L1) where L1 is bound to (1,2,3,4). When eval is called
explicitly we are actually evaluating twice. The argu-
ment of eval will evaluate to (length L1) which on being
evaluated again will give the length of list L1 which is
4.
It is this ability of LISP to construct programs on the °y
by manipulating data that attracted the early AI (arti-
¯cial intelligence) researchers to this language. In the
roughly two and a half decades from 1960 to 1985, LISP
It is this ability of
was the language of choice when working on AI prob-
LISP to construct
lems. Actually, multiple companies developed special-
purpose hardware in the form of LISP machines which
programs on the
ran LISP natively. However, these companies withered
fly by manipulating
when LISP implementations on standard hardware im-
data that attracted
proved substantially and standard hardware rapidly gai-
the early AI
ned in power.
(artificial
intelligence) The interpreter EVAL can be easily written as a func-
researchers to this tion in LISP. Actually, it will look like a cond with a
language. large number of clauses that will handle each primitive

218 RESONANCE ¨March 2014


GENERAL ¨ ARTICLE

function and the special functions (like defun, setq,


quote). The default clause in cond will evaluate all
arguments of list L (its lone argument) and then ap-
ply the function represented by the ¯rst argument to
the remaining evaluated list elements as actual argu-
ments. Notice that this di®ers from ¯-reduction in the
¸-calculus where the evaluation is normal order and the
¯rst ¯-redex chosen for reduction is the outermost one.
However, EVAL evaluates the actual arguments ¯rst be-
fore applying the function. In ¸-calculus this is called
applicative order reduction.
McCarthy's orginal description of the interpreter is less
than two pages. It is probably the shortest description
3 Object oriented languages be-
of the semantics of a programming language.
gan with SIMULA. Smalltalk was
5. LISP: Evolution, In°uence and Prospects also an early and influential OO
language. In such languages one
In the early days of LISP it was actively developed and can define classes which are

many dialects that did not inter-operate came into exis- templates or types for a group of
objects. An object is some en-
tence. The two major dialects were MACLISP and IN- capsulated data along with func-
TERLISP. Consequently, in 1984 Guy Steele with help tions (often called methods) that
from a large number of Lispers created the book Com- define the behaviour of the ob-
mon Lisp. The Language. A second edition came out ject. Good examples are com-
mon data structures like stack,
in 1990 [7]. In 1994 Common Lisp was standardized as
queue and binary tree. Many
an ANSI standard. Objects have been added to LISP older languages have acquired
through CLOS (the Common Lisp Object System) and OO features – for example C (as
this is also part of the ANSI standard. CLOS di®ers C++), Python, Lisp, Fortran (For-
from standard Object Oriented (OO) languages3 like tran95 and later).

Java and C++ in signi¯cant ways. It has a dynamic


object system that allows class creation and change at
execution time. LISP also has a powerful, dynamic de- LISP was used widely
bugger that makes it a much easier language to use. for artificial intelligence
LISP was also the ¯rst language to introduce a powerful programming in its
macro system that encouraged many users to create new early days. Two well-
control structures and entire domain-speci¯c languages known early programs
on top of LISP (see [8]). are Terry Winograd's
SHRDLU and Carl
LISP was also the ¯rst language that had a garbage
Hewitt's Micro-Planner.

RESONANCE ¨ March 2014 219


GENERAL ¨ ARTICLE

LISP was the first collector. Garbage is the name given to memory that
functional was once used by the program but cannot be accessed
programming from the running program anymore. Since lists are con-
language and as tinuously created and destroyed in LISP programs it is
such is a necessary to reclaim memory that is garbage. Other-
precursor of more wise, it is quite possible that the program will run out
recent functional of memory and thereby crash. OO programs also be-
languages like have in the same way where objects are created and then
SML and Haskell. become inaccessible. Most modern functional and OO
languages have garbage collectors (e.g., Java, Smalltalk,
SML, Haskell), but C++ (also C) is an exception. It
expects the programmer to manage the memory by ex-
plicitly deleting objects that will not be required any
longer. This is the single major source of bugs in C++
programs { the memory leak. While garbage is being
collected the program does not do useful work. So a lot
of e®ort has been expended in designing e±cient garbage
collection algorithms and in identifying garbage at the
point at which it is created so that it is cheaply collected.
Much of this research was done in the context of LISP
since it was the ¯rst mainstream language with garbage
collection.
LISP was the ¯rst functional programming language
and as such is a precursor of more recent functional
languages like SML and Haskell. These languages are
strongly typed with a type inference system unlike LISP.
However, they are largely functional languages and lack
the multi-paradigm dimension of LISP.
4 EMACS is a very versatile ed- LISP is still used in AI research though it is not the dom-
iting system that has ELISP (or inant language in that area. It is also the base language
EMACSLISP) as its scripting
language. This makes it pos-
of the EMACS editing system4 . While it is unlikely
sible to customize EMACS to do that it will achieve the prominence it had in its earlier
some very complex things like years, modern LISP is a standardized, multi-paradigm
create an integrated program language that has many good quality implementations
development environment. An- (some of them free) that are actively maintained. It has
other widely used program that
uses a dialect of LISP as a script-
many books and good documentation support and re-
ing language is AutoCAD. mains a language that one can pro¯tably choose to write

220 RESONANCE ¨March 2014


GENERAL ¨ ARTICLE

large systems, especially exploratory ones that require


rapid prototyping and powerful language features.

Suggested Reading

[1] John McCarthy, Recursive functions of symbolic expressions and their


computation by machine, Part i, Communications of ACM, Vol.3, pp.184–
195, 1960.
[2] Alonzo Church, The Calculii of Lambda Conversion, Princeton Univer-
sity Press, 1985.
[3] Stephen Cole Kleene, Introduction to Metamathematics, 7th Ed., North-
Holland, 1974, Seventh printing.
[4] Alan M Turing. On computable numbers, with an application to the
entscheidungs problem, Proc. of the London Mathematical Society 2,
Vol.42, pp.230–265, 1937.
[5] Christos Papadimitriou and Harry R Lewis, Elements of the Theory of Address for Correspondence
Computation, Prentice-Hall, 2nd Ed., 1997. Harish Karnick
[6] J Roger Hindley and Jonathan P Seldin, Lambda Calculus and Department of Computer
Combinators, an Introduction, Cambridge University Press, 2008. Science and Engineering
[7] Guy L Steele, Common Lisp, the Language, Digital Press, 2nd Ed., 1990. IIT Kanpur
[8] Eugene Charniak, Christopher K Riesbeck, Drew V McDermott, and Kanpur 208 016, India.
James R Meehan, Arti cial Intelligence Programming, Psychology Press, Email: [email protected]
2nd Ed., 2013. Updated for Common Lisp, First Edition 1978.

RESONANCE ¨ March 2014 221

You might also like