Programming Languages (CSC-302 98S)
Outline of Class 21: Functional Programming: The Scheme Perspective
Held: Wednesday, March 11, 1998
- LISP (for LISt Processing language)
was developed by John McCarthy of MIT in the late 1950's as a language
for artificial intelligence programming.
- It is considered one of the primary functional languages and has
- At the time LISP was developed, many researchers suggested that
- symbolic manipulation and
- organization of symbols in hierarchical structures.
- In developing a language to support "intelligence", McCarthy chose to
- symbols as basic data types
- built-in lists and list-manipulation operations
- numbers and strings as basic data types
- Noting that the application of a function to arguments is similar to
listing the function and its arguments,
McCarthy also chose to use the same notation for function applications
and list structures.
- An open paren
- The elements of the list (or the function and its arguments), separated
- A close paren
- This is an extreme application of von Neumann's assertion that programs
are data: in LISP, the same structure can be treated as a list at one
point and a function application at another.
- McCarthy chose to use a prefix notation for all function operations,
including "primitive operations". In LISP, we write
(+ 2 3)
for "add two and three".
- LISP is often interpreted. Most LISP interpreters use the
read-eval-print model. That is, the interpreter
- Reads an input expression or function definition.
- Evaluates the expression.
- Prints the result.
- In LISP, function order is done in applicative (also known
as eager or innermost) order: before functions
are applied to their arguments, those arguments are evaluated.
- This is similar to call-by-name.
- It is often helpful to classify the types of functions we use in
writing LISP programs.
- Constructors build data structures.
- The most common constructor is
cons which builds lists.
- Destructors and Selectors which extract
components from data structures.
- The most common selectors are
head which selects the first element of a list and
tail which selects all but the first element.
- Predicates which return boolean values.
- Most predicates check types or attributes of their arguments.
atom? checks whether something is an atom.
- The power of McCarthy's design is demonstrated by LISP's longevity.
LISP and LISP variants are still dominant languages for AI programming
(and for other types of programming).
- Most (all?) LISP-like languages provide a number of functions for
building and modifying lists.
- LISP builds its lists with cons cells, pairs of pointers.
- The first pointer gives the data.
- The second pointer gives the rest of the list.
- The empty list is
- The predicate that checks if a list is empty is usually
- One constructs lists with
(cons x x) builds a list
with first element
x and remainder
- Those of you who like to think about what's happening in memory can
think of this as allocating a new cons cell whose left pointer is
the object and whose right pointer is the list.
- Those of you who prefer a higher-level view can think of this as a
form of "prepend".
- In many variants, one can build longer lists with
- You can get the first element of a list with
- You can get the all but the first element of a list with
cdr? Because in the original
machine LISP was implemented on, it was easy to load the cons cell into
the address and data registers. "
car" is "contents of
address register". "
cdr" is "contents of data register".
- In many languages,
car is called
cdr is called
- Scheme is a variant of LISP that "cleans up" some of the problems of
the original LISP.
- Scheme provides
- Static scoping (the original LISP was dynamically scoped).
- A formal semantics (the original LISP was informally defined).
- Streams, a form of "infinite" lists (which had been suggested
as an add-on to the original LISP).
- A way to package up "the rest of the program". These are
- A host of other features.
- Scheme is now defined by committee. The most current definition
is the fifth revision, which has been a few years in the making.
- Scheme provides a number of basic operations
- In Scheme, we define "global" variables with
(define id exp).
- Similarly, we define "global" functions with
(define (fun param1 ... paramn) exp)
- We use a commercial version of Scheme in the MathLAN, Chez Scheme.
- Chez Scheme provides a simple interactive interface which you can
get by typing
scheme at any prompt.
Compute the factorial of n
(define (fact n)
(if (= n 0) 1 (* n fact (- n 1))))
Add an element to the end of a list
(define (addend x l)
(if (null? l)
(cons (car l) (addend x (cdr l)))))
Reverse a list (yes, it's built-in, but we can still define it)
(define (reverse l)
(if (null? l)
(addend (car l) (reverse (cdr l)))))
A Tail-recursive reverse
(define (tailrev l r)
(if (null? l)
(tailrev (cdr l) (cons (car l) r))))
(define (reverse l)
(tailrev l ()))