Functional Programming Lessons in Imperative Code
A BlogBook is a collection of blog posts intended to be viewed as a sort of book on their own.
This BlogBook is on how to transfer the lessons that can be taught by strict, pure functional programming (think Haskell) into imperative programming languages.
This blog book is completely written but not yet completely posted.
Introduction to Applying Functional Programming Lessons in Imperative Code
I recommend to any professional programmer that they spend some time with a very strict functional language, enough to get to the point they can write non-trivial programs in it.
For the purposes of these posts, you can read functional programming as Haskell. There is an earlier definition of functional programming that just involves having closures, but as all modern1 languages have closures now that is no longer a relevant definition. There are other languages like O’Caml, Clojure, and Scala that have very functional flavors, but there are few languages in common use that take it to the extreme of Haskell.
I believe the lessons learned there can be carried back into any conventional imperative language. That’s hardly a controversial claim. What will be controversial is that I believe that many people are carrying very superficial lessons back while missing the deeper ones.
In this series of posts, which I am gathering up into what I call a “BlogBook” (see link in header if you’re reading this through the blog post), I propose to piss off the entire programming community one way or another.
-
I will anger those who believe there is nothing to learn from functional programming, or that a Sufficiently Smart Programmer 2 could just work everything I’m going to say here out from first principles without ever leaving their first language.
There will be a several things that I’m going to cover that we may not “learn” from Functional Programming, but it can still put a new spin on it. For instance, I would agree “global variables are bad” is something we can (and historically did) figure out just from the imperative world, but an FP-based viewpoint can enrich that view. There’s also some things imperative programming will eventually teach you, but FP will ramp up to 11 and teach you much more quickly, by forcing it on you. So bear in mind as you read this that I am never claiming this is the only way to learn these lessons, merely a good and fast one.
- I will also anger people who believe that any claim that is not accompanied by a complete history of everything related to the claim going back to Aristotle is implicitly claiming to have invented the idea from whole cloth with no previous inputs. There aren’t all that many such people numerically but they sure do like to make angry posts online about how dare they claim to have ideas.
-
I will anger those who are very attached to strict functional programming by pointing out some places where I think it gets some things wrong. Even, sometimes, “less wrong than conventional imperative programming”, but still wrong.
-
I will most especially anger those people who think that “practicing functional programming in a conventional languages” means using lots of maps and filters and making vague references to “monads”, but they really only mean
Option
. (I have already written about what monads are, and this post includes why I believe they are not that useful in your imperative language). I will anger these people because in an entire series of posts about the lessons we can learn from functional programming I am still going to call nested maps generally an antipattern in imperative languages, and things like pipelines generally a parlor trick rather than the Most Important Lesson of Functional Programming.So, you know, brace yourself for that. It’s a ways away, though. We’ve got a lot of ground to cover first.
-
I will anger people who can’t process generalizations that have exceptions. “Imperative languages” is a big set. I don’t know every language any more than anyone else does, but when I generalize assume I may know the exceptions. For instance, one of my first generalizations is that trying to program imperative languages like Haskell, with lots of small functions everywhere, results in a parenthesis explosion. Ruby may be flexible enough that this is not the case, to some extent also Perl, assuming normal levels of effort (e.g., no weird source rewriting). Assume my points hold if it is true of most imperative languages. Listing every exception I know, which of course will still miss others, is just tedious for everyone, so please take this particular example as indicative of general understanding of the concept of exceptions. Discuss them, sure, but don’t assume that just because Lua (for example) has some particular feature that implements something I claim imperative languages generally do not invalidates the point I’m making.
I will base the code samples in this on Go, for a couple of reasons:
- It’s the language I’m using the most lately, though I’ve done Python and Perl lately too.
- It is probably the least “multiparadigm” modern language in any sort of popular use, and the one most syntactically hostile to what looks like “functional” code, so if a lesson from Functional Programming can be imported into Go, it can be imported into anything. It is the worst case scenario for “functional programming”.
Right. Enough throat clearing. Let’s get on with it.
-
I will arbitrarily define “modern language” as one born after 1990 or so, so this includes Python and what we today call the dynamic scripting languages, and several modern static languages, but excludes C, and to a large degree, C++. Although C++ is such a mess that even figuring out if it has a given feature can itself devolve into a language lawyer cage match. ↩︎
-
In deliberate parallel to the mythical Sufficiently Smart Compiler. ↩︎