Over the past year and a bit I've been trying to really 'grok' the functional way of programming computers. I've been doing it the 'other' (imperative) way for over 30 years and I figured it's time for a change, not because I expect to become suddenly super productive but simply to keep life interesting. I also hope that it will change my perspective on programming. Think of this piece as an intermediary report, a postcard from a nice village in functional programming land.
So far, it's been a rocky ride. I'm fortunate enough to have found a few people on HN that don't mind my endless questions on the subject, I must come across as a pretty block-headed person by now. But that won't stop me from continuing and from trying to learn more about this subject. RiderOfGiraffes and Mahmud deserve special mention.
My biggest obstacle is that I came to the world of programming through that primitive programming device called the soldering iron. Before learning how to program I used '74 series logic chips', which are all about state and changing state. Transitioning from logic chips to imperative programming is a reasonably small step. It never really occurred to me there was another way of doing things until about two years ago. This is a major shortcoming in myself I think, I find something that works, and then I'll just go crazy using it, not bother to look outside of my tool chest to see if there are better ways (or different ways) of doing things as soon as I find something that works.
The first thing that I ran in to was 'anonymous functions'. This was a bit of a misnomer I think, because after all, after ld and strip are done with your program all functions are anonymous, whether your language supported anonymous functions or not. So that threw me for a loop for a bit until I realized that we're talking about functions that simply have their body appear in the place where normally you'd call a function, and since they're only there once you don't need to 'name' them, hence the 'anonymous'.
So, for a 'die-hard' C programmer that has never seen an anonymous function in their whole life, that's the thing you wished you had when you call 'quicksort' with a function that is used only once to determine the ordering of two array elements. Every time you call quicksort you end up defining a 'compare' function for two elements, and that function is typically only used in that one spot. If C had had anonymous functions you could have coded it right in there but because it doesn't you need to create a named function outside of the quicksort call and then pass a pointer to that function.
What's not entirely obvious from the simplicity of having functions without names is how powerful a concept anonymous functions really are, I think this is one of the hardest concepts of functional programming to truly understand. Once you have anonymous functions and a few other essentials in your arsenal you can build just about any program, including recursive ones, even when at the language level your language does not explicitly support recursion. This is something that I never really properly understood until recently, after banging my head against the wall for a long long time. Math is not my strong suit, so to me 'functions' are bits of code that do something and then return a value. The whole concept of a function returning another function seemed alien and strange to me at first (and to some extent still does), but I'm getting more comfortable with it.
Next up is assignments. In imperative programming languages the first thing you learn about is assignment, in functional programming it is the last. It's quite amazing how far you can go in functional code without ever seeing 'the left hand side' of the expression. Typically in imperative code you'll see this pattern:
functiondefinition nameoffunction (bunch of parameter)
. somevariable = someexpression
. anothervariable = anotherexpression using somevariable
.
. [optionally someglobal = yetanotherexpression, print something or some other effect]
.
. return finalexpressionIt's a fairly simple affair, you start at the top, evaluate the expressions one by one, assign the results of the sub-expressions to temporary variables and then at the end you return your result. Side effects are propagated to other parts of the program by changing global state, doing output or other 'useful' operations. Every time you see that '=' sign you are changing state, either locally for local variables or with a greater scope. In functional programs it is also possible to 'change state' but you normally speaking will only do that very explicitly and very sparsely because you'll find that you don't need to.
The 'someglobal' assignment from the example above is called a side-effect, the function does more than just compute a value, it also modifies the state of the program in a way that is unpredictable from the calling functions point of view. It's basically as if the world outside the chain of functions has been influenced by the function call, instead of the function simply returning a value.
This is one of the easiest pitfalls of 'imperative' coding to fall in to, and it is closely related to spaghetti programming, modifying state left right and is a bad thing, just like 'goto' when used indiscriminately. Now, nothing stops you from programming in a 'functional style' in a language like C or some other 'imperative' language that does support function calls (and most languages do). This probably will have the side-effect (grin) of you spending less time debugging your code, plenty of time the subtle and invisible changes of state by code at a low level can be a source of some pretty mean bugs.
Because functional programming is all about functions calling other functions which compute values and so on you'll find that there is much less need to store intermediate results explicitly. You still need variables to pass parameters in to functions, but on the whole I find that if I re-write the same bit of code in a functional style that most of my 'local' variables simply disappear. I've been pushing this very far (probably too far) in a little test website that I built using that good old standby PHP, it is the last language on the planet to be described as 'functional', and yet, if you force yourself to concentrate on writing side-effect free code you can have a lot of fun by simply trying to avoid the '=' key on your keyboard.
Of course, in the end the only way a computer can do anything useful is to change state somewhere so eventually you'll have to 'give in' and make an assignment to store the result of all the work the functions have done but it's definitely an eye opener to see how far you can get with this side-effect free code.
Evaluating functional code is very hard when all you've seen your whole programming career long is imperative code. Another stumbling block on the path to enlightenment you will run in to, for a person that already knows how to program 'imperatively', is to unlearn years of conditioning on how to simply read a program. You have to train yourself to start the understanding of code you're looking at from the innermost expressions, which are the first to be evaluated. This is hard because you've conditioned yourself to read code like you would read a book, from the top to the bottom, and from the left to the right.
We like to think of computers as 'automated humans reading a text and executing instructions line-by-line', all this conditioning can be a significant obstacle, you have to first 'unlearn' that habit, which can be quite hard. And 'all the bloody parentheses' don't make that easier, it's sometimes hard to see the wood for the trees. I feel that I'm slowly getting better at this, but it certainly doesn't feel natural just yet.
So far this intermediary report on the travels in the land of functional programming. The natives are nice, the museums contain interesting exhibitions, I think I'll hang around here for a bit longer to see what else I can find.
Maybe I'll even settle here, if I find that I can do what I am already able to do in a way that is more fun, more reliable or more productive, or any combination of those. But that's still a long way off.