Dave said:
Actually I'd call LISP is a mixture or hybrid of imperative and
functional, for precisely this reason. Classically its functional
parts got more emphasis and IINM use precisely because there were
(plenty of) other choices for imperative.
Lisp surely has accreted many imperative constructs over the
years. But essentially Lisp started with just the Lambda Calculus,
widely acknowledged as the original basis for functional
programming. The direction was the opposite of what's suggested
above - Lisp started as almost completely functional, and
imperative features were added and got more use because of
other imperative languages.
Even classical LISP has lists (really conses) and two kinds of atoms:
number (integer) and symbol (name). Later ones, including the standard
Common LISP, have several distinguishable number kinds, character
strings, and more.
Yes, maybe it's an oversimplification to lump those
all together under just "atom". But no user defined
types.
Primitive arithmetic functions are usually
polymorphic over number types, EQUAL effectively is over all types,
and some others have special handling or special cases that are
arguably polymorphic like (CAR NIL) => NIL.
Operators in C apply to multiple types, but that doesn't
make C polymorphic.
When I say Lisp doesn't have polymorphism what I mean is
that there are no linguistic mechanisms to support defining
polymorphic functions. Saying Lisp is polymorphic is like
saying C is polymorphic because a function can switch() on
one member of a struct and cast another member to one type
or another.
Not to mention optional OO
dispatching which provides 'true' polymorphism among OO types.
Lisp object systems are layered on top of Lisp, not part
of Lisp. An OO language can be implemented in C, but
that doesn't make C an OO language.
Although user functions are canonically (and importantly) 'just
lists', primitive or compiled functions are usually a distinct type,
something like #EXPR, and closures at least sometimes are.
Probably an important implementation detail, but it
doesn't seem like much more than that.
Well, you can capture the data (needed) for a closure, as indeed you
can in almost any language, but it doesn't have any behavior.
Behavior can be included with the data by means of
a function pointer, which is the key point. This
often can't be done in other languages, when they
lack first class function values -- as in Pascal, for
example.
Agree there. Although I'd have to be under severe stress to think it's
worth the trouble when languages designed for it are available.
In my experience most programs have some parts that are better
done functionally, and other parts that are better done
imperatively, no matter what language they're written in.
I think people who do no functional programming in C
"because it isn't a functional language" are missing out
on an important part of program construction.