Posted on September 12th, 2015
I recently came upon an ACM Turing Award lecture by John Backus entitled “Can Programming Be Liberated from the von Neumann Style? A Functional Style and Its Algebra of Programs”. It got me thinking about the idea of functional versus imperative (aka “procedural”) programming.
To quote Backus, “An alternative functional style of programming is founded on the use of combining forms for creating programs. Functional programs deal with structured data, are often nonrepetitive and nonrecursive, are hierarchically constructed, do not name their arguments, and do not require the complex machinery of procedure declarations to become generally applicable.” A simple example brought up in the lecture to highlight the difference is that of computing an inner product:
c := 0 for i := i step 1 until n do c := c + a[i] x b[i]
Def InnerProduct $\equiv$ (Insert +) $\circ$ (Apply to all $\times$) $\circ$ Transpose
Notice the use of dummy variables in the first case to solve the problem. I won’t go over the details, but in summary, imperative programming is more concerned with how to get things on typical modern hardware, whereas functional programming focuses on what is being done. The latter is often lauded for its expressive power, lack of “side effects”, and much more.
A key challenge I’ve had (and perhaps still have) in understanding the difference is that, in reality, the dividing line is quite blurred. There are imperative languages that incorporate functional ideas such as list comprehension, and vice versa. I’ve read that the legend Donald Knuth even argued that anything functional can be written in an imperative language, so why even bother1. That said, the language you use guides your thinking, and can have a massive effect on the code you produce, in my experience. Thus, my recent projects in Go (imperative) have rekindled my curiousity about functional programming, which has led me to dive into Haskell - considered to be a somewhat “pure” functional language.
Don’t get me wrong; I love Go. Its approach to concurrency is extremely elegant. I think it has the best dependency pattern out there, designed for the modern age - imports built with the modern web in mind. Go is unbloated and well though-out. However it really isn’t designed for functional programming. This has refined the aforementioned dividing line, prompting me to notice the differences more accutely.
for loops would be some examples. The bottom line is I feel like I get more obtuse and less creative programming imperatively; my inital foray into Haskell has made me feel sharper and more creative. Like I said, I haven’t changed, but the language paradigm can help guide your thinking.
Procedural programming follows naturally from the design of modern computer hardware. You have memory, and you have a thing that reads, computes, and writes to memory. This is amenable to stuff like
for loops that use dummy variables. You’re literally iterating through memory and computing something. This feels like running around a jungle trying to accomplish some task, at least to me. It is the most evident way to initially design programming languages - based on the hardware. But as with many things, the initially evident way could turn out not to be the best for solving the problems at hand. I wonder whether functional programming represents a move in that direction.
I don’t think procedural code is necessarily “easier”. Perhaps at first, but much more important is habit - our minds are extremely malleable machines after all. Perhaps imperative languages dominate purely for historical reasons. Or perhaps functional programming does require a somewhat more “mathematical” mind. I’m not sure. But for whatever reasons, the latter is generally considered to be more abstract and esoteric. Let’s take that as given for now, without necessarily presuming the reasons.
My question is, does a more obscure but arguably more powerful programming paradigm make sense when working on a collaborative project (hence the title of this post)?
On the one hand, approaches like that of Go really make sense. It is designed for simplicity2 that scales well. This means that a company like Google (where the language originated) can hire fresh grads and have them pick it up very quickly. It means that a massive group of programmers can collaborate more effectively: package dependencies are more transparent, and it is relatively easy to dive into other people’s code (or one’s own code from a while back). It’s also an overall relief to have fewer semantics and syntactical idiosyncracies to deal with.
On the other hand, the hypothetical thought of starting a company that uses a language like Haskell terrifies me. Think of the obstacles: it’s already hard enough for startups and companies to find technical talent. Try finding such talent that is also adept at functional programming.
And yet, after quite some deliberation, I think it might be advantageous nonetheless. First of all, I’m increasingly convinced that a programming paradigm should be “powerful”, allowing for mathematical abstraction without too much obscurity. I’m tired of writing repetitive code that feels like plumbing. A team of versatile functional programmers could be productively lethal. Secondly, if indeed it is the case that they are on average sharper, or at least encouraged by the paradigm to think more sharply while creating, does choosing a language like Haskell not create favourable selection when “recruiting”? One could end up with an environment and team that produce safer, more effective code.
Ultimately, the tools should serve the objective at hand. I have no doubt that there are areas where functional programming excels, and others where an imperative language might be a better choice. I also think that any reasonably capable thinker can teach themselves to do either or both. I am however increasingly convinced that machines should aid man (and woman) in their creative process, allowing us to focus on the abstractions of what is being done, rather than the plumbing. I can’t wait to see Haskell at work, at work.