
Functional Programming Doesn’t Work (and what to do about it) - twampss
http://www.planeterlang.org/en/planet/article/Functional_Programming_Doesnt_Work_and_what_to_do_about_it/
======
silentbicycle
This is one of a series of posts by someone with decades of experience doing
game programming. He's also very fond of Erlang (he's been using it since
1999!), and he's exploring how well it works for a domain he knows, but which
is quite far from Erlang's typical niche.

Try reading it (and his archives, at <http://prog21.dadgum.com/archives.html>
) in that light. It's not just somebody ranting about how FP is junk after
halfheartedly trying to learn Haskell. The "Purely Functional Retrogames"
series is particularly good.

I would love to see more blogs that have people seriously evaluating how
"newer"* languages hold up for _real work_ , not just page-sized Greenfield
projects. As he puts it, "Timidity does not convince."
(<http://prog21.dadgum.com/35.html>).

* I know, Erlang isn't that new.

~~~
Davertron
I just read all of the "Purely Functional Retrogames" posts, and was sorely
disappointed that there's no source. Did I miss something here? The posts were
interesting on a high level, but basically gave me blue-balls.

------
yason
Purely functional programming, that is. Not functional programming per se.
With that corrected, I agree with the guy.

However, his critique shouldn't be surprising to anyone but blind believers.
Paradigms and features all have comfort zones and none of those is large
enough to cover even nearly everything.

Analogously to strong/weak typing and dynamic/static binding, you rarely only
want either. You either start with strong and/or static and build yourself
dynamic and/or weak behaviour by hand. Or you start dynamically and later
enforce certain type constraints deemed appropriate. There are a few holy wars
in between but you can raise above that.

Similarly pure functions and impure state management ought to support each
other, not fight each other. Pure functions are pure, clean, and functional so
that mutations can be sparse and well-controlled. Conversely, the state must
be destructively updated in some place by the impure code so that the pure
functions are relieved from having to bother with it.

Similarly you can start at either of the pure and impure ends but you're bound
to end up somewhere in between for any useful program.

You can start with pure functions and build something with them and worry
about saving the state later. The original author is right in that that I
often start with the latter and feel somehow guilty when writing the state. I
shouldn't! Personally, I've noticed that this approach too often equals
gigantic mutations on huge, monolithic states that yield huge, monolithic new
states that you have to store somewhere.

Alternatively you can start with impure code and clean out stuff into pure
functions as you learn how the program evolves. Each time I do this in, for
example, Python I get the sense of how fucking brilliant I am. I should do it
more often! Personally, while "erring" on the impure side at first I've
noticed that the resulting program is often more beautiful as pieces of pure
art rise from a pool of impure goo.

------
gruseom
I almost didn't realize that this is the Dadgum guy. (The original URL for
this post is <http://prog21.dadgum.com/54.html>, and it's easier to read to
boot.) He has vast experience and his articles tend to be excellent. If he has
misgivings about FP they are worth taking seriously. Edit: it's clear in
context that his misgivings are about 100% pure FP, and he still favors a
nearly-entirely-functional style.

~~~
ntoshev
The links in the URL you give actually work, unlike the ones at planeterlang.
Thanks!

~~~
rubyrescue
PlanetErlang reproduces whatever is in the rss feed. I just switched the RSS
feed of ErlangInside.com to summaries yesterday because anytime i publish
something they get the credit (and the backlinks).

------
swolchok
I'd like to see a rebuttal to the core criticism:

 _Imagine you’ve implemented a large program in a purely functional way. All
the data is properly threaded in and out of functions, and there are no truly
destructive updates to speak of. Now pick the two lowest-level and most
isolated functions in the entire codebase. They’re used all over the place,
but are never called from the same modules. Now make these dependent on each
other: function A behaves differently depending on the number of times
function B has been called and vice-versa.

In C, this is easy! It can be done quickly and cleanly by adding some global
variables. In purely functional code, this is somewhere between a major
rearchitecting of the data flow and hopeless._

I am almost certain a Haskellite will say something about monads or arrows,
but I don't know what.

EDIT: I guess another criticism might be "when would you possibly need this?".
It smells suspiciously like a symptom of bad design -- these two totally
independent, low-level calculations are now dependent on each other. A
possible use case might be a tweak to an artificial physics, as in a
simulation or game.

~~~
DarkShikari
I think it works both ways.

There are inherently some changes that are extremely hard to make to a well-
constructed functional program, and there are equally some changes that are
hard to make to a well-constructed procedural program.

I would hypothesize there is no language paradigm in existence that provides
optimal efficiency for implementing all possible types of architecture
changes.

~~~
gruseom
I think what you're saying here is close to what the OP is arguing. The title
obscures this a little bit, but it's clear from the second paragraph that he's
arguing against _purely_ functional programming.

------
coffeemug
Err, his example is misleading - that's the problem with generalizing and
ignoring nuances. The problem here is purely syntactic. Any functional
language worth its salt supports lexical scoping, which means symbols in
different scopes can shadow each other. So, I can easily say something like
this:

    
    
      if a > 0
      then let a = a + 1
           in # do smtg with a
      else # do smtg with a
    

Of course the inner _a_ within the _let_ clause is a "different" _a_ , because
outside of the _let_ clause the old _a_ remains. This doesn't have to be slow
either, because if the compiler detects that the outside _a_ isn't used
anymore, it can simply increment a memory location or a register.

With a little bit of syntactic support from the language, you could easily
write this in a natural, comfortable form, which will then automatically get
compiled to the code above. In Haskell, it's pretty easy to do that with
monads:

    
    
      do $ when (a > 0)
           a <- a + 1
    

(I haven't touched Haskell in a while so there might be some minor syntactic
issue here, but the overall point still stands). The point here is that the
number of potential branches can be determined at compile time, which makes
the problem syntactic.

A bigger problem is when you're dealing with iteration, and you can't know at
compile time how long the loop is. Then you can't unroll it, which means the
compiler implementors can't use the technique above. But they can use a
different technique - recursion, which can later be compiled back to iteration
via tail calls optimization, removing any awkwardness. Again, with the _do_
monad, it's trivial in Haskell.

Of course the question is, if you go through all the trouble to build up the
beautiful, elegant mechanics that make it possible to write imperative code
that gets compiled to purely functional code, that gets compiled back into
imperative machine code for efficiency - what's the point? IMO the point is
mathematical beauty - something you can't easily put a price tag on. There are
some empirical benefits as well, but I'm not sure if they outweigh the
troubles. Anyway, there _is_ a great argument here, but the author only
scratches the surface of the problem and doesn't dig nearly deep enough to get
to the meat of the problem.

~~~
davidw
That example actually appears fairly regularly in Erlang code.

    
    
        A1 = ...
        A2 = ...
    

And it leaves a bad taste in one's mouth.

I see putting stuff in a variable as a way to 'take a breather' in the middle
of code:

    
    
        A = some(hairy(function(that() + does() * lots()));
        B = back(into(A) + the(A) fray());
    

I guess that's not the best example because A is used twice and you could just
pop the 'A' calculation out into its own function, but sometimes it's nice to
make code easier to read by breaking stuff into discrete steps that are easy
to read later, rather than one huge line that does everything and then returns
it.

~~~
silentbicycle
The "A1 = ..." thing is a convention from Prolog, from which Erlang inherited
its single-assignment variables (and many other relatively unusual features).
People who write code with several levels of X1 = ..., X2 = ... vars are
probably writing Erlang (or Prolog) with a thick imperative accent. It's like
writing Python for loops using integer indexes - unless there's a specific
reason to do so, it's a sign that the author is probably new to Python.

While putting intermediate steps of a calculation into variables can help
clean up the code, if there's any sort of conceptual significance to that
value, it's worth choosing a better name than X1. "ATC" (with "Avg. Triangle
Count" as an end-of-line comment), for example, would actually mean something.

------
baguasquirrel
This guy may be smart, and he may have done a lot, but this article seriously
smells like a troll. Isn't this exactly the sort of behavior you're _not_
supposed to engage in?

Two library calls, deep in vastly different parts of the code. And you're
going to make them mess with each other? For christ's sake, they're used
everywhere, they've been working fine for ages, and now you're going to mess
with them, make them intertwined and thus complicated, for one stupid edge
case.

You know if you really wanted to do it, you could do an unsafePerformIO,
but...

...I'm just not buying it. I can think of many ways that FP (not just pure FP)
is deficient, but this is not one of them. The biggest problem I see with FP
is that when your data model is sparse, imperative-style state change updates
are just more efficient from a programming perspective.

I should note that I'm not the first one to spot this. It was talked about
amongst the FPers I knew in school. Lets just say each variable, every
function input and function output is a vertex in a graph. When I say that the
data model is sparse, what I mean is that the edge count on the vertices is
small. How small? I dunno, it's a gut feeling sort of thing, or maybe someone
could correct me on it.

------
yummyfajitas
_Now make these dependent on each other: function A behaves differently
depending on the number of times function B has been called and vice-
versa...In C, this is easy!...It’s easy to mechanically convert a series of
destructive updates into what’s essentially pure code._

These are both easy tasks in Haskell. Just use the state monad. You also get
100% certainty that the state isn't leaking into places where you didn't want
it to leak to.

Lack of controlled mutable state is a failure of Erlang, not functional
programming.

Also, counting function calls would actually be fairly straightforward in
Erlang as well. You create a process which counts function calls to B. A then
queries this process when it needs to find out how many function calls were
made to B. Slightly _harder to program_ , but it works even in the distributed
setting.

~~~
almost
That's not quite fair though, the problem as he states it is to do this
_after_ you've written the program. So while yes, you could use the State
Monad that might not be a trivial change to the code. Things might be even
worse if the function was already written in another Monad, Monad Transformers
can be just a little tricky. Unless of course you write everything in the
State Monad just in case you ever have to do anything like that :)

------
alrex021
This is really a misleading title.

Should have been something more like:

    
    
      "Purely Functional Programming doen't always work"

------
antirez
Everything pure in programming will tend to have similar problems I guess. I
like languages where imperative, object oriented and functional constructs can
be mixed together, and I think Ruby is one of the best languages from this
point of view.

~~~
elblanco
Upvoted for pragmatism. I like Python for this as well.

~~~
kburn
upvoted because that comment made too much sense to sit at 0.

------
DavidSJ
_function A behaves differently depending on the number of times function B
has been called and vice-versa.

In C, this is easy! It can be done quickly and cleanly by adding some global
variables. In purely functional code, this is somewhere between a major
rearchitecting of the data flow and hopeless._

In Common Lisp, I'd DEFVAR a variable with dynamic scope, then LET its value
be incremented on each call to B. I'd never use SETQ or its variants. There's
no major re-architecting here, and no breakage of the functional style.
Dynamically-bound variables are like implicitly passed-through arguments to
every function call.

~~~
lutorm
Um, in purely functional code isn't this _by definition_ not possible? As in
that the definition of "functional" means that _it has no side effects_ and
the return value depend only on the input values?

~~~
DavidSJ
With dynamic variable binding, there are no side effects, because the LET is
undone as soon as its scope is exited.

Think of dynamic variables as constituting merely the implicit passing of a
symbol->value hash table as an argument to every function call, and a LET as a
local modification to this hash table.

So:

(defvar a 10)

(defun x () (if (= a 0) t (let ((a (- a 1))) (x))))

behaves the same as:

(defun x (&optional (a 10)) (if (= a 0) t (x (- a 1))))

------
omouse
The second example, single assignment form, is useful for walking through the
function based on time/computation-sequence. Very good for making assertions
in the middle of the code somewhere.

 _edit: I forgot to mention that Dijkstra had some complaints when Functional
Programming first reared its
head,[http://www.cs.utexas.edu/users/EWD/transcriptions/EWD06xx/EW...](http://www.cs.utexas.edu/users/EWD/transcriptions/EWD06xx/EWD692.html*)

~~~
baguasquirrel
I can say: a' = a + 1

What's so confusing about that? This is not a shortcoming of FP.

~~~
omouse
Yeah, it was a weird thing for him to mention.

------
setori88
Take a look at mozart-oz, a multi-paradigm/model programming language. it is a
rich language able to perform OOP, dataflow, functional, declaritive and and
can hide observable non-determinism. Though it might not be fast enough to
handle your particular case of gaming. grab "Concepts, Techniques, and Models
of Computer Programming" for a read.

------
epall
This is why I like Clojure's pragmatic approach. FP doesn't solve every
problem easily, but often your core challenge is handling state transitions.
Clojure attacks mutable state better than most languages out there, lets you
get functional when you want to, but still allows for reasonably easy
imperative code for things like Java interop.

------
omaranto
The problem the author finds with pure functional programming is very well
known and is one reason people never use a pure functional language, sticking
instead to practical functional languages such as Erlang (that has processes),
ML dialects (that have references), Haskell (that has IORefs and
unsafePerformIO), Scheme and Common Lisp (that have full support for
imperative programming), Clojure (that has Vars, Refs, Agents and Atoms), etc.

I think the culture surrounding each of those languages does look down on
using the mutation features (and for good reasons), but it's not like the
message is "never use them", but rather "don't use them if you don't have to".

Maybe the fact that he doesn't emphasize that his gripe is with _pure_
functional programming and calls it plain "functional programming" is a little
misleading.

------
DanielBMarkham
For the reading disinclined: _It’s not a failure because of soft issues such
as marketing, but because the further you go down the purely functional road
the more mental overhead is involved in writing complex programs._

------
tetha
Somehow, the links in the article do not work for me (they all link back to
the same article, and searching with various techniques for those links) does
not result in anything useful.

But, overall I guess some FunctionalWeenie learnt about NoSilverBullet, to put
it into C2-wiki terms (so don't take the mild burn too serious :) ).

~~~
gruseom
No. This guy is the real thing. He has been doing hard-core game programming
since the early 80s and serious Erlang hacking since 1999 (!), and has a long
record of thinking deeply about programming and writing about it. This is one
of the few programming bloggers worth taking seriously.

Oh, and the links work fine in the original post
(<http://prog21.dadgum.com/54.html>).

~~~
tetha
Yes, I am reading the original posts now and he is right, the preceding posts
are posts I remember reading and they do change the light shed by the
articles.

------
bena
Ok, I can't extend this into a ridiculously long series of blog posts about
"what I've learned" etc. but I can sum up this and every other remotely
similar article about how such and such technology or method is insufficient
for every task:

"No one thing is good for everything."

------
revoltingx
I'm a bit of an erlang noob, but my projects have started to reach the
complexity where keep track of the flow of information gets convoluted.
However, the advantages that it (FP/Erlang) offers as far as providing side-
effect free scalable code is too great to ignore. I understand that in his
case he needs very fast access to data that 'should' be globally accessed. In
which case a global variable makes sense. So far, it has been a bit annoying
not having 'global' variables in Erlang. (not even in modules)

However, good languages like Erlang offer many alternatives. Mnesia has near
real-time access speeds, which you could for example implement in your render
thread/process. It also has things like per-process dictionaries/hash, and ets
for dirty and even faster access to 'global' data.

I suspect that a lot of it has to do with the architecture of the code. I tend
to prototype fast, so I run into these issues often. But I tend to keep mental
notes as to how the code would be better broken off into modules, reduce the
number of passed arguments, (and makes those to the fastest possible
primitives.) and move any 'global' data to mnesia.

