
Lisp is not functional (2010) - saint-loup
http://letoverlambda.com/index.cl/guest/chap5.html
======
orthecreedence
> In fact it can be argued that lisp is one of the least functional languages
> ever created

Come on. Returning a value from an (if ...) already makes it more functional
than a lot of languages. Sure, it's not functional in the sense Haskell is
functional, but you could certainly write all your code in a functional manner
using it, abstracting out any side effects via macros. I would say it's fairly
well-geared towards functional programming if one was determined to use it for
such a thing.

That said, I wouldn't call lisp a "functional" language. I'd call it multi-
purpose. It CAN be functional, it CAN be imperative, it CAN be object-
oriented.

It's certainly not the least functional language ever created though.

~~~
reikonomusha
The author very clearly defined what he means by functional, and he is
correct, idiomatic Lisp is not. IF being a value-returning expression only
_encourages_ functional programming in the author's sense, since functions
with conditionals can be written without explicit control flow where you can
sneak side effects in. But, to be clear, flow control doesn't affect
functional purity, and so it can be said that IF expressions vs. statements
don't actually provide more or less functional behavior.

With side effects rife within Lisp's standard library, and with things like
dynamic variables included with the language, I have a difficult time
believing that it would be easy to "abstract out" side effects with some
macros, while simultaneously providing a convenient, broad set of capabilities
to program with. At that point, you might as well use macros to compile a
whole new DSL within Lisp.

~~~
eikenberry
Defining an already used word to mean something precise and then arguing based
on that is fine when constrained to a subset of technical readers who are used
to this idea (you see this a lot in philosophy papers), but when publishing to
a general readership you can't expect people to just accept that.

~~~
reikonomusha
I would agree with you if the author did not explicitly say what he meant, and
assumed the reader knew what he meant, but the author did not make any such
assumption.

In order to quell the issue of "functional" having many definitions, the
author chose one, and wrote about it. I don't see your alternative, which
seems to be "accept that functional means something different and write about
_that_ ", as one that is worthwhile to pursue.

~~~
harry8
Maybe to acknowledge that "functional" is used with a different meaning,
often, would be sensible rather than just pretending all one needs to do is
re-define it from common usage and proceed.

People are not compilers. If you want to redefine a word that is already in
your readers' vocabulary with a meaning ascribed you've kind of got to talk
about why you're doing that and why your redefinition is worthwhile and the
alternative, generally accepted meaning needs to be jettisoned. Or you risk
unintentional impersonation of a crank, which would be a shame if you're
actually correct.

~~~
pyrale
The point here is that the definition in the article is not something new. The
author merely chooses to pick an accepted definition of functions, and build
from there.

You makd it look like he's trying to push a new definition, which is not true.

------
kazinator
This is mostly wrong. Lisp is mistermed "functional language" due to schools
which teach freshmen some subset of a Lisp dialect which they must use in a
pure way to solve homework problems: no variable assignment, no loops. The
second thing you hear from people after "functional language" is things like
"oh yes Lisp. I remember all the recursion; it was elegant".

Lisp is in fact organized around the idea of a function being a pure function
and it was that way from the beginning.

This is why the sequential execution of forms with side effects has dedicated
macros/operators with names like _prog_ and _progn_. These provide "the
program feature": giving step by step procedural instructions that tell the
machine what to do, rather than an expression which computes a value. The only
reason to use (progn x1 x2 ... y) is that x1 x2 ... have side effects. If they
only compute values that are discarded, the whole thing may be optimized down
to just _y_.

Also, there is a funny quirk in the terminology whereby Lisp people still
refer to the arguments of _cond_ as being " _cond_ pairs", even though you can
write procedural bodies consisting of multiple forms, and it has been that way
for decades:

    
    
       (cond
         (this-condition do-this and-do-this yield-this-value) ;; triplet, not pair
         (that-condition do-that and-do-that yield-that-value)
         ...)
    

The " _cond_ pairs" terminology persists because _cond_ was originally
restricted to pairs! Except for the aspect that it evaluated the conditions in
left to right order, it was inherently oriented toward handling the cases in
functional programming: upon finding a true expression, it evaluated its
result counterpart, and yielded that value. In "pure Lisp" programming, _cond_
will still be used that way.

Of course, even ancient Lisp had _rplaca_ and _rplacd_ to mutate conses, and
_set_ and _setq_ to mutate variables. The 1960 Lisp 1.5 manual describes
arrays (indexed from zero (damn it!) and mutable).

Calling Lisp "least functional" is completely silly, since it was the first
tool which enabled computer scientists to translate pure recursion on paper
directly into executable code. There are countless languages which might take
such a title, by virtue of having no support for functional programming.

~~~
reikonomusha
_Common Lisp_ , which the author is referring to, definitely was _not_
organized around functions being pure. Common Lisp actually doesn't have a lot
of high-order functions either. A notable exclusion was COMPOSE.

McCarthy's original Lisp was organized around purity, but that preceded Common
Lisp by many decades. And, while some names carried over from the original
Lisp, Common Lisp had no intention to replicate all of the intents and virtues
of McCarthy's Lisp. Even before CL was made, millions of lines of application
code had been written in a number of dialects, which had nothing to do with
translating math or anything like that.

~~~
kazinator
Common Lisp has a functional argument in every library function that takes a
_:test_ and/or _:key_. (Even if these are not specified, they are understood
to have functional default values like _# 'identity_ and _# 'eql_). And of
course _apply_ , _mapcar_ and other applicator friends have a functional
arguments. All these things tend to be front-and-center in everyday Lisp
programming, hardly "also have" features.

~~~
nitroll
Having higher order functions is not really what makes a language functional.

------
metaphorm
in the functional programming community I find an obsession with these two
things

1\. what counts as functional programming (and more specifically "why lang X
is not really functional")

2\. inventing new jargon, particularly in cases where existing terminology is
mostly adequate, but some hair-splitting detail is not properly covered by a
word already in use. this tendency might be a natural consequence of spending
all day on macro expansions.

~~~
svachalek
TL;DR what mathematicians call functions, programmers call pure functions, and
nobody told the author.

It's like someone saying to a scientist, "that's just a theory, man."

~~~
pron
> what mathematicians call functions, programmers call pure functions

Even that's not precise. What mathematicians call _computable_ functions,
programmers call _total, pure_ functions, and even then only as an
approximation.

------
lispm
I use usually this classification:

* a language enables functional programming: maybe Python, Java 8, ...

Provides some functional programming constructs like lexical closures, but the
main programming style is something else.

* a language supports functional programming: Lisp, ...

Has a lot of support for functional programming (closures, higher order
functions, ...) and the library/software is using it extensively. One can
develop in a mostly functional subset.

* a language enforces functional programming: Miranda, Haskell, SML, ...

Most of the language organised around functional programming including all of
its library and code. It's the dominant paradigm.

------
hellofunk
Depends on the lisp. CL is not so functional, it is multi-paradigm. Clojure,
however, is mostly functional.

The complexities surrounding state in CL are not unlike any other non-
functional language. I find it hard to call a language functional if it
doesn't strongly encourage immutability (or require it) and, preferably, have
persistent data structures.

It's for these two reasons that I don't think Swift is functional, and I don't
consider CL to be functional.

~~~
delish
There's a big group of hackers who refer to Common Lisp as "Lisp" or "lisp." I
believe Doug Hoyte is doing that. Richard Gabriel and Peter Norvig do it too.

They might say, "Clojure is a lisp," but when referring to "Lisp" (i.e.
without an article "a" or "the") they're talking about CL.

~~~
hellofunk
And there's a big group of academics, nearly all of them, that refer to lisp
as a family of languages with many dialects.

~~~
iak8god
> And there's a big group of academics, nearly all of them, that refer to lisp
> as a family of languages with many dialects.

Certainly Doug Hoyte, the author of this phenomenal ~400 page book on Common
Lisp programming, is aware of that.

~~~
hellofunk
Well, opinions may vary, but I own this book and the word phenomenal is not
appropriate. Most CL hackers I know do not take this book too seriously. It is
loaded with pretense and self-congratulatory comments.

~~~
iak8god
Yes, opinions vary. I found LOL very instructive, but maybe I was in just the
right place when I discovered it.

In any case, what I was responding to in your earlier comment was the
implication (I thought) that Hoyte doesn't understand "lisp as a family of
languages with many dialects." But since you've read LOL, that cannot have
been your point...

~~~
hellofunk
My point was more to the readers of HN who wouldn't know, and the title just
says "Lisp is not functional" so I was providing some context.

~~~
iak8god
Thanks for clarifying. I can see how it would be confusing for someone to be
dropped into chapter 5 of the book with a section title like that.

Common Lisp hackers may use "Lisp" colloquially as a shorthand for the
language they're working with ("Common Lisp" is a bit of a mouthful, after
all), but I don't think many of them are confused about the existence and
nature of other lisps, or that they tend to disagree with academics on this.

------
lngnmn
Practicality beats purity.

Without setcar! setcdr! and friends R4RS Scheme is functional, and with these
it is mostly-functional, which is good-enough.

Racket, BTW, evolved _mcons_ to make this distinction explicit.

Common Lisp, obviously, cannot be called functional, but it is trivial to
derive a good-enough mostly-functional subset of Common Lisp. Actually, CL,
being a kitchen sink for many dialects of Lisp, notably the one from
Symbolics, cannot be as balanced and refined as early Schemes used to be,
because it incorporates lots of "destructive" primitive procedures, without
marking them explicitly in any standardized way or packaging them in a
separate package, which, arguably, would make writing a mostly-functional CL
code much easier.

------
Animats
It's certainly possible to write procedural LISP. I'm currently debugging some
from the 1980s. It's 1970s-80s style code - too many global variables and
little structure larger than the function. At the time, only LISP had dynamic
allocation and garbage collection. Today, you could write this code in
anything from Javascript to Go.

~~~
dang
What program are you debugging?

~~~
Animats
[https://github.com/John-
Nagle/pasv/tree/master/src/CPC4](https://github.com/John-
Nagle/pasv/tree/master/src/CPC4)

------
paulddraper
By Haskell standards, the C language is functional

[http://conal.net/blog/posts/the-c-language-is-purely-
functio...](http://conal.net/blog/posts/the-c-language-is-purely-functional)

(pointing out why rigorous classification is silly)

------
amelius
Title should be: "Lisp is not purely functional".

------
javajosh
_> The real purpose behind functional programming is to separate the
functional description of what should happen from the mechanics of how it
actually does happen._

I found this statement entirely fascinating, as it reminds me of the
definition and motivation of _interface_ in Java and many other languages. And
no-one has ever claimed Java to be a functional language because of
interfaces!

~~~
aassddffasdf
No, people claimed that Java was a functional language, prior to actually
getting lambdas & all that comes with them, due to [anonymous] inner classes.

~~~
bbcbasic
The goalposts keep moving.

------
cronjobber
Outside of heroic whole program compilation efforts like Stalin (telling hero
worship there), Lisp can't optimize programs written in the functional style
subset to the degree actual functional languages do.

Maybe we can call Lisp a slow-functional language.

~~~
kazinator
Well, perhaps _strictly evaluated_ functional languages.

------
lisper
This is complete and utter hogwash.

> Clearly lisp procedures are not functions

Some are. Others are not.

The only thing a language needs to be legitimately called a functional
language is for functions to be first-class data objects. It's helpful but not
necessary for them to be lexically scoped. It is also helpful but not
necessary to have a conditional expression. Common Lisp has all of these
things. In fact, Common Lisp has all of the features commonly associated with
modern functional languages except static type inference (and you can add that
if you really want it).

Common Lisp is not a _purely_ functional language, but that's true of pretty
much every language except Haskell.

~~~
kazinator
I think you can achieve Turing completeness without higher order functions,
using only primitive recursion. That tells us that whatever can be calculated
with the help of higher order functions can be somehow calculated without
them, without resorting to destructive manipulation.

~~~
lisper
You don't need higher order functions, but you do need lexical closures. C
only has one stack, so it's a PDA. Strictly less powerful than a TM.

This is the reason lexical closures are so important. They are what let you
"allocate" an infinite amount of memory so you can be Turing-complete without
side-effects.

~~~
kazinator
It isn't clear that C is just A PDA. Without any destructive manipulation, C
functions can use pointers to pass down and maintain a chain of contexts, so
that each function potentially has random access to something at each stack
level above it without having to pop anything.

~~~
lisper
Yes, that's true. But you can only do a finite number of dereferences from the
current frame without being destructive.

~~~
kazinator
Not so; you can recurse back up over that structure.

~~~
lisper
It's not quite that simple. It turns out that you're right, but it's much more
challenging than just saying "recurse back up". The problem is that if you're
recursing then you're not in the current frame. You can only do a finite
number of dereferences _from the current frame_. That makes every frame
effectively a PDA cell.

Now, that is not a proof (yet) because there's an additional wrinkle: pointer
dereferencing lets you reach an arbitrary distance back up the stack in a
single step, and that might be the thing that takes you from PDA-land into TM-
land. But you only have a finite number of pointers in the current frame, and
each of those can only be dereferenced a finite number of times in the current
frame and so this is equivalent to just having a local copy of that finite
amount of information in the current frame and so you're still a PDA.

Now, it turns out that argument is actually wrong. But figuring out why it's
wrong is not so easy. It makes an interesting exercise writing an emulator for
a two-stack machine in purely functional C. But you're right, it can be done.

~~~
kazinator
Indeed, I can have a function like this:

    
    
       get_data_from_stack(parent_pointer, num_levels);
    

this function just recurses, decrementing num_levels until it hits zero, and
chasing the parent pointers. With that, I have random access all over the
stack.

Of course, C is ultimately a finite state machine because pointers have a
fixed width. "sizeof pointer" is a constant expression. The absolute number of
objects which can be addressed is finite. In an abstract program which doesn't
use sizeof on pointers or work with their internal representation in any way,
we can relax this model to allow an unlimited space; we just understand that
we can have as many stack frames as we want and that somehow, the & operator
in any one of them produces unique pointers.

~~~
lisper
No, that doesn't work. Because...

> Of course, C is ultimately a finite state machine because pointers have a
> fixed width.

Yes, but I'm willing to let that slide as long as you never do anything with a
pointer but dereference one or compare two of them to see if they're equal
(because you have to assume that to make _any_ interesting computational model
Turing-complete). But I'm not willing to let num_levels slide. If num_levels
has a fixed width (and in C it would have to unless you show me how to build a
purely functional bignum library) this solution won't work.

It really is not trivial to figure out how to make this work.

------
ww520
Lisp is certainly functional.

If exclusively pure function is the litmus test for functional language, then
most languages including Haskell are not functional.

~~~
reikonomusha
Haskell's functions are almost always pure, except for some ordained "special"
constructs. If we use purity to test whether a language is functional, it
would be very disingenuous to not call Haskell "functional" under the purity
definition, despite some satellite features it includes.

The telling sign of this fact is that you can actually use equational
reasoning and substitution as a method to _prove_ statements about your
Haskell programs.

~~~
pron
Haskell's functions may be mostly pure but they're certainly not total, so
they're still not the familiar mathematical concept, and therefore would fail
the author's definition as well.

------
pron
Arguing over such definitions is futile and silly, because there's bound to be
some approximation, some papering-over at some point.

Let's start with the author's definition of functional programming, _a
functional language is a programming language made up of functions_ , and
let's say we agree. Now, what is a function? The author addresses that, too:
_A function is a concept from math that has been with us for centuries: A
function is a static_ (??) _, well-defined mapping from input values to output
values._ The first, and relatively minor, problem here is what one means by
"well-defined". Usually, precise definitions are given in a specific
formalism, and different formalisms have different definitions of a function.
For example, the classical definition of a function is the set-theoretic one,
which says that a function is a (usually infinite) set of ordered pairs, i.e.,
a relation, with the property of being single-valued. This set is sometimes
called the _graph_ of the function. Obviously, finite computers can't store
such infinite structures, and so they can't represent functions in the
classical sense. Functional languages usually define functions as algorithms,
allowing to represent a subset of the classical function called the
computable, or constructive functions. Those functions also correspond to
(some) classical functions, so this is indeed a minor issue.

The problem is that programming languages don't even define constructive
functions. For example, the function of the square root is computable, but it
(at least its real-valued version) is only defined on the nonnegative reals
(let's not worry about representing the reals for now). Another example is the
maximum function over a set of naturals, which is only defined for finite sets
(and a maximum function over sets of _integers_ is even more complicated).
However, most programming languages, even the functional ones, allow their
"functions" to be applied to objects _outside_ their domain, something that
does not make sense. So functional programming languages call their
"functions" _partial_ functions, but partial functions, unlike classical or
even constructive functions, are no longer the same well-known mathematical
object. So in Haskell, for example, sqrt and max are not functions of the
familiar kind, and neither are log, div and many other "mathematical"
"functions". Ironically, it is C that treats some functions (such as division)
most similarly to math by declaring that division by zero is undefined, i.e.,
doesn't have to mean anything. Of course, meaningless in math is very
different from meaningless in programming, as a program will have _some_
observable behavior even ruled meaningless.

Next, we can restrict our programming language to actual computable
mathematical functions, sometimes called in the programming community _total_
functions (to distinguish them from _partial_ functions). Instead of
declaring, like in C or in math, that a program that compiles may mean
nothing, and instead of specifying a meaningful yet very unmathematical
behavior, we can forbid meaningless things (like applying a function to an
argument outside its domain) from being accepted as a well-formed program.
This is possible, at least in principle, in several ways, one of them
(probably not the most convenient) is with dependent types. But doing so is
still problematic. For one, if a programming language only allows total
functions, the programmer must _prove_ to the language that the argument is
indeed in the function's domain. This can be very, very hard (undecidable in
general), and part of the reason why no such language is actually used to
produce real software (with a couple of minor exceptions that only prove the
rule). The other problem is that even then, the objects called functions in
the programming language only represent mathematical functions as an
approximate denotation, as they really express computations, and computations
are not functions -- they are processes that consume time and memory.

To demonstrate the difficulty further, consider that in math, two functions
are equal iff their mapping is the same. In those total functional languages
this is not necessarily the case, and there are different kinds of equality.

So if you want to be mathematically precise, you can never really program with
functions, or at least not the familiar mathematical function. At some point
in the definition of "functional" \-- whatever definition you use -- you're
going to have to make some concessions and approximations. I think that it is
well accepted by now that "functional" is a spectrum, and one that includes
Lisp.

A short and nice discussion of this can be found in section 1.1 of Jean-Yves
Girard's _Proofs and Types_ [1]. I don't think anyone can accuse Girard of not
knowing what functions and functional programming are.

[1]:
[http://www.paultaylor.eu/stable/prot.pdf](http://www.paultaylor.eu/stable/prot.pdf)

------
oppositelock
Articles like this really make me think of the No True Scotsman fallacy.
[https://en.wikipedia.org/wiki/No_true_Scotsman](https://en.wikipedia.org/wiki/No_true_Scotsman)

Yes, compared to pure lambda calculus, lisp is not purely functional. In the
space of programming languages, it's a functional programming language more so
than any other kind.

~~~
reikonomusha
The author defined what he meant by functional, and by that definition, he is
correct.

If you change the definition of "functional" to "a language with first class
functions and stateful closures", as you seem to implicitly want, then yes,
Lisp is functional.

~~~
jedimastert
I suppose, then, it's a bit closer to moving the target

------
jegutman
That's some nerdy click-bait there.

------
DonHopkins
Lisp is fun.

