I have some comments on the recent http://www.defmacro.org/ramblings/fp.html
article on functional programming, and I decided I might
as well post them here and send the author a pointer
rather than just putting them in an email.
(You are approaching a skirmish in the language wars! Hit *BACK* now!)
I like functional programming, and I liked the
http://www.defmacro.org/ramblings/fp.html article, but I think he
probably should present more disadvantages up front.
One of the reasons that I prefer Common Lisp to Haskell (and why
others prefer ML variants to Haskell) is that there are things which
are clumsy or slow to express in purely functional form. Okasaki's
_Purely Functional Data Structures_ is a marvellous book well worth
reading for other good reasons, but one smaller benefit of reading it
is that some such things come through there.
One annoying performance issue for me is hash tables; in an imperative
language, it's straightforward
to use the idiom of hash index into a modifiable
collection to get O(1) lookup. Good luck doing this in FP! And, if you
tell people that they should rewrite their BDD packages for
reliability and clarity and shareability in Haskell instead of C++,
and tolerate the extra O(log2 1E7) performance hit of doing all their
cache lookups in nice purely functional search trees instead of ugly
imperative hash tables, you will encounter some sales resistance ---
though perhaps not as much, in the eventual runout,
as if you conceal this performance issue from them and
let them discover it for themselves.:-|
The issue of making small incremental modifications to large indexed
or cross-linked data structures isn't just an algorithms performance
issue, it can also make programs difficult to read and think about. I
have done a lot of work on programs to play the game of Go, where on
each move a player places a stone on one of the 361 points on a board.
At various times I have written complete programs in C++, CL, and
Haskell. In the imperative languages there is some programming hassle
involved in making the changes undoable (so that
you can try a variation,
then backtrack to the starting point to try another),
whereas you get undoability for
free in Haskell; but in Haskell
I found considerably more hassle in
trying to express the small changes without
doing a deep copy of potentially very
large cross-linked data structures.
It seems to me that this is a
fundamental issue, not just a symptom of
my naivete about functional programming.
It might not be a big issue for an Erlang telephone switch,
because I expect
most of the state in such a switch is tied to an
individual call, interacts weakly if at all with the
state of other calls, and goes
*poof* when the call ends. But if you tried to write, say,
a MMORPG server
in a purely functional language, I would expect that the ongoing small
modifications to the
complicated shared global state of the world
would be a source of programming pain.
Also, purely functional languages seem to be unusable without laziness
(to create cycles) and the purely functional languages people have not
convinced me, as a casual user, that their handling of laziness is
completely ready for prime time in large hairy systems. The difficulty
of debugging lazy code is a minor issue; the difficulty of bounding
the performance (especially heap usage) of complicated lazy code is
worrisome, a potential showstopper. I would be very nervous about
planning to develop a large Haskell system for something complicated
and not similar to existing FP software (perhaps the MMORPG server
example) without doing some serious investigation into that. I think
it might be easy to end up with a server which failed from heap
exhaustion when lazy-variable placeholders held pointers into futures
which "common sense" shows could never happen, but which aren't
manifestly unreachable and so therefore aren't garbage collected. Both
my intuition and my superficial reading of the mailing lists suggests
that such bugs are not hard to create, and can be hard to test for and
hard to debug. The existence of various large FP systems successfully
"used in anger" is reassuring, but only incompletely so: it's not hard
for me to come up with a story why from the ability of version control
systems and telephone switches and compilers to manage this problem it
does not follow that it's manageable for all systems.