Functional Programming Doesn’t Work (and what to do about it)

Programming in the 21st Century - James Hague - December 28, 2009

Read suddenly and in isolation, this may be easy to misinterpret, so I suggest first reading some past articles which have led to this point:




Admitting that Functional Programming Can Be Awkward


Follow-up to “Admitting that Functional Programming Can Be Awkward”


Back to the Basics of Functional Programming


Purely Functional Retrogames


Puzzle Languages


How I Learned to Stop Worrying and Love Erlang’s Process Dictionary




After spending a long time in the functional programming world, and using Erlang as my go-to language for tricky problems, I’ve finally concluded that purely functional programming isn’t worth it. 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. That sounds like a description of programming in general—problems get much more difficult to solve as they increase in scope—but it’s much lower-level and specific than that. The kicker is that what’s often a tremendous puzzle in Erlang (or Haskell) turns into straightforward code in Python or Perl or even C.




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.




A second example: It’s a common compilation technique for C and other imperative languages to convert programs to single-assignment form. That is, where variables are initialized and never changed. It’s easy to mechanically convert a series of destructive updates into what’s essentially pure code. Here’s a simple statement:

if (a > 0) {
   a++;
}

In single-assignment form a new variable is introduced to avoid modifying an existing variable, and the result is rather Erlangy:

if (a > 0) {
   a1 = a + 1;
} else {
   a1 = a;
}

The latter is cleaner in that you know variables won’t change. They’re not variables at all, but names for values. But writing the latter directly can be awkward. Depending on where you are in the code, the current value of whatever “a” represents has different names. Inserting a statement in the middle requires inventing new names for things, and you need to make sure you’re referencing the right version. (There’s more room for error now: you don’t just say “a,” but the name of the value you want in the current chain of calculations.)




In both of these examples imperative code is actually an optimization of the functional code. You could pass a global state in and out of every function in your program, but why not make that implicit? You could go through the pain of trying to write in single-assignment form directly, but as there’s a mechanical translation from one to the other, why not use the form that’s easier to write in?




At this point I should make it clear: functional programming is useful and important. Remember, it was developed as a way to make code easier to reason about and to avoid “spaghetti memory updates.” The line between “imperative” and “functional” is blurry. If a Haskell program contains a BASIC-like domain specific language which is also written in Haskell, is the overall program functional or imperative? Does it matter?




For me, what has worked out is to go down the purely functional path as much as possible, but fall back on imperative techniques when too much code pressure has built up. Some cases of this are well-known and accepted, such as random number generation (where the seed is modified behind the scenes), and most any kind of I/O (where the position in the file is managed for you).




Learning how to find similar pressure relief valves in your own code takes practice.




One bit of advice I can offer is that going for the obvious solution of moving core data structures from functional to imperative code may not be the best approach. In the Pac-Man example from Purely Functional Retrogames, it’s completely doable to write that old game in a purely functional style. The dependencies can be worked out; the data flow isn’t really that bad. It still may be a messy endeavor, with lots of little bits of data to keep track of, and selectively moving parts out of the purely functional world will result in more manageable code. Now the obvious target is either the state of Pac-Man himself or the ghosts, but those are part of the core data flow of the program. Make those globally accessible and modifiable and all of a sudden a large part of the code has shifted from imperative to functional…and that wasn’t the goal.




A better approach is to look for small, stateful, bits of data that get used in a variety of places, not just on the main data flow path. A good candidate in this example is the current game time (a.k.a. the number of elapsed frames). There’s a clear precedent that time/date functions, such as Erlang’s now(), cover up a bit of state, and that’s what makes them useful. Another possibility is the score. It’s a simple value that gets updated in a variety of situations. Making it a true global counter removes a whole layer of data threading, and it’s simple: just have a function to add to the score counter and another function to retrieve the current value. No reason to add extra complexity just to dodge having a single global variable, something that a C / Python / Lua / Ruby programmer wouldn’t even blink at.



Categories: Blogs  Programming in the 21st Century  

Comments

anonymous avatar

Why in the world would you write 3 to 5 lines of code when one will suffice?

if (a > 0) {
  a1 = a + 1;
} else {
  a1 = a;
}

should be

a = (a > 0) ? a + 1 : a;
or
a1 = (a > 0) ? a + 1 : a;

It’s controlled, it’s functional, and it’s perfectly readable. 

I don’t understand why people make these things so much harder than they need to be, even when making a point.

As for your point:
>> Making it a true global counter removes a whole layer of data threading..

Unless you’re using a singleton, globally scoped variables are near-terminable offenses, IMHO, especially if repeated.

Seriously, there is no reason for, in the case of a game, a score to be globally scoped. It’s all about accessibility that makes sense, and a good design up front solves that, every time.

Posted by Randy on 29 Dec 2009 at 08:45



 
anonymous avatar

Yes, FP is hard if you start with this premise:

“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.”

This is BAAADDD!!!!

That means that these functions are codependent, they are highly coupled, and they need to be redesigned.

For FP to work, each function result can only depend of the input parameters (which can be another function too).

If your app architecture requires that highly coupling functions (and I would doubt first the architecture that has that requirement), then FP is the wrong tool for the job… but then again, I would distrust the relationship between those functions first.

Posted by Jorge on 29 Dec 2009 at 15:07



 
anonymous avatar

@jorge

The level at which the codependency is bad is too abstract.  It might not be an app architecture rule, it can be an 11th hour business rule change, or a human interface improvement for tracking consecutive mistakes, etc.

If you need to have a fully realized understanding of the problem at hand, and the solution to that problem, in order to employ FP effectively, that’s a very crippling requirement for a vast majority of software projects.  I don’t think the situation is quite that bad, but I agree with the authors point.

Posted by jmoiron on 29 Dec 2009 at 16:00



 
anonymous avatar

Jorge: it’s not BAAADDD if it’s a requirement.  Complexity should be minimized and managed, not shunned.

Posted by stanley on 29 Dec 2009 at 16:08



 
anonymous avatar

lol, you’re pretty bad at this aren’t you.

Posted by JamesDees on 29 Dec 2009 at 16:18



 
anonymous avatar

In your second to last paragraph did you really mean “...and all of a sudden a large part of the code has shifted from imperative to functional”.

If I understand you then wouldn’t the shift “Make those globally accessible and modifiable…” have happened the other way around?

If not then, surprise surprise, I didn’t understand you.

Posted by Robert on 30 Dec 2009 at 01:51



 
anonymous avatar

@stanley

If you know the final specification of any system before you write it then you are not working on the same kind of systems that I am.  My users are constantly changing/adding to the spec in ways that totally break even java code (where you obviously have globals/statics etc to hack around stuff) and mean you need a major rewrite.  This doesn’t go down well with users if you have to do a major redesign cos they asked for F1 to depend on F2 as in the original example, especially if they know that they could just fix it if the system was written in their favourite language (VB).
If you’re going to get functional languages into mainstream then this is an important topic that needs proper coverage imho.

Posted by Dave on 30 Dec 2009 at 02:02



 
anonymous avatar

Uh, all six of those URLs at the top of the post seem to point to the same article (this one).  Could someone fix the links?

Posted by whir on 06 Apr 2010 at 22:12



 
anonymous avatar

I have never known that functional programming will not be much useful and now I am quite surprised because there are many people who actually preach about the benefits of functional programming. I believe that these guys are going wrong somewhere which they may realize that a very later stage. - Jordan

Posted by Fabian on 21 Nov 2010 at 02:59



 
anonymous avatar

Previous comments have covered the Func A dep on Func B problem - a very poorly chosen example - and a strong clue that you have not fully grasped the basic ideas of the functional paradigm.  You’ve also missed the idea of binding and expressions in a functional context.  In erlang, I have so rarely written an “if” block like the one you presented I could probably count the number of times on one hand.  An “IF” statement in an imperative language is an IMPERATIVE construct.  In functional langs like erlang it is just a short-cut version of CASE.  And CASE is just a guarded expression.  And in good functional fashion we are only really interested in the VALUE of expressions.  In that sense we “try” to avoid side-effects in our funcs because that is something more than just a simple value.  Its not so much that its harder to think about programs that have side-effects, its that its harder to think about programs where side-effects are firing off all over the place and harder still to promote them the (now prevailing) concurrent context.  It is unrealistic to have “pure, pure, pure” functional code - perhaps a reason why erlang (which I consider a hybrid) thrives commercially and haskell largely remains an intellectual curiosity,

Posted by Divotbasher on 31 Dec 2010 at 14:14



 
anonymous avatar

I have to agree with Randy. Good design trumps almost anything. I was talking to my father the other day and he was telling me about the “old day” when he had to program on punch cards with 16k of memory on the computer. Things had to be precise and there was no room for excess. Now with so much hard drive space and memory available to programmers, they have gotten lazy and messy with their code. When my father was working on disaster recovery software the name of the game was no excess code, even if the developers thought it was easier.

Posted by Johnson on 21 Feb 2011 at 16:48



 
anonymous avatar

Thanks for providing this information. It’s quite informative and helpful. Most especially to those notices users. Two thumbs up for the contributor.Best Binoculars Under 100

Posted by jimmy on 21 Feb 2011 at 21:15



 
anonymous avatar

awesome article

Posted by Vlad on 28 Feb 2011 at 12:55



 
anonymous avatar

Can’t seem to view more than a few minutes of this presentation, then it dies - tried more than 10 times over the past couple of days.
Duvet Sets
Do you have a mirror of the presentation anywhere?

Posted by David T on 25 Mar 2011 at 20:40



 
anonymous avatar

Well I think both of the first commenters have made valid points on their claims. Regards, Angel of territory mapping software company.

Posted by Angel on 22 Jun 2011 at 19:41



 
anonymous avatar

I’m having the same issue with the presentation cutting off after about 10 minutes. Anybody knowhow to resolve the problem? I’m on firefox 3.6 if that helps. Steve from san diego virus removal

Posted by Steve Wilkers on 25 Jul 2011 at 03:31



 
anonymous avatar

I liked the posts and cool layout you have here! I would like to thank you for sharing your experience and the time it took to post!! Two Thumbs up!
dolce gabbana perfume

Posted by Nasha on 26 Aug 2011 at 11:02



 
anonymous avatar

I just want to say that it is an elegant blog that have useful information. I will visit this site again for more information. Take care & carry this job of sharing nice information with others.
ripenaxrau hjd electroluxap fgd tpersonalcar

Posted by dtyczka on 09 Nov 2011 at 07:45



 
anonymous avatar

This doesn’t go down well with users if you have to do a major redesign cos they asked for F1 to depend on F2 as in the original example, especially if they know that they could just fix it if the system was written in their favourite language (VB). i don’t agree with that at all.
Bail Bonds Los Angeles

Posted by Steve on 12 Jan 2012 at 13:58



 
anonymous avatar

I have to agree with Randy. Good design trumps almost anything. I was talking to my father the other day and he was telling me about the “old day” when he had to program on punch cards with 16k of memory on the computer. Things had to be precise and there was no room for excess. How to Consolidate Debt

Posted by Jessica on 12 Jan 2012 at 15:33



 

 1 2 >


Add comment

Name:

Email:

URL:

Smileys

Remember my personal information

Notify me of follow-up comments?