Faoileag's Nest

My Home On The Net

Functional Programming - Lessons learned

Look, that's why there's rules, understand? So that you think before you break 'em.
(Lu Tze in Terry Pratchett's "Thief of Time")

Intro

When I started software development in 1982, the standard paradigm in the home computer world was imperative / procedural programming, with BASIC, Pascal or with assembly language (Z80 in my case).

Then came the 1990s and object-oriented programming became mainstream with Borland Pascal and later with C++, and C++ is still the language I work with on a day-to-day basis 25 years later (doing gui development with Qt for a living).

But I always wanted to go beyond that and understand the third programming paradigm, Functional Programming, as well.

In "Hackers and Painters" Paul Graham praised Lisp as a language you can be extremely productive with when developing bug-free software fast.
I knew that Emacs plugins were written in Lisp, and functional languages seemed to be on the rise in the new millenium as well: Clojure appeared for the java vm and F# for .NET. So I decided I should give it a try as well, if only to learn one more tool for the trade.

But I didn't get it. Not with Lisp, not with Clojure. The whole functional thing eluded me through multiple attempts to learn the stuff.

Then, during a lockdown in early 2020, I tried again, and this time something went 'click' in my head and I understood. I understood functional programming, I understood enough of Lisp, and I understood the limitations of the paradigma.

This essay is a short summary of what I have learned in that year.

Functional Programming is a paradigm, not a language

On hindsight, not realizing that was probably what kept me from "getting it" on my earlier attempts.
I used to think that for functional programming you have to use Lisp (or a more modern language like Clojure or F#). And that Lisp's unusual notation was a necessity for functional programming. It is not.

Instead, functional programming is a paradigm, meaning that you do functional programming when you adhere to the rules defining that paradigm and not just by using a specific language. In fact, you can use any language you want, as long as it supports the programming style necessary for functional programming. Be that C++, Lisp or Haskell.

Programming in Lisp is not functional programming

Well, at least not automatically. You can do functional programming in Lisp, but the interpreter does not mandate, let alone enforce it. You can write great imperative or procedural code in Lisp as well, and it may even look very 'lispy' with lots of brackets, but it is not functional programming.

In fact, it is so easy to write imperative / procedural code in Lisp, that I would regard it as a trap when you come from the object-oriented world and start writing code in Lisp.

No objects in Lisp? Bah, who needs objects. Global variables, and some functions to manipulate them, will provide a good alternative.

Only, that's not functional programming. My first programs in Lisp were like that. And it took me a while (and some further reading) to realize that. But in the end I saw where I had been wrong, and so my current project is done in functional programming. In purely functional programming.

Purely functional programming is taking functional programming that one step further

What's so magic about those six letters purely that they mandate a wikipedia page of their own?
Well, whereas in mere "functional programming" the developer has a lot of leeway to do things, in "purely functional programming" he has not. Instead he has to follow the two basic rules of functional programming religiously. Any deviation and it's no longer purely functional programming.

These rules are:

  1. All functions must always deliver the same result for the same arguments.
  2. Functions must have no side effects.
Simple enough, eh? Only it isn't.

Reading input from the keyboard violates rule one. So does a function like random() that delivers a random number.

Printing something to the console violates rule two, as output to a terminal is considered a side effect.

There you are. Now go ahead and write a meaningful program that adheres to these rules.

You can't write meaningful programs in Pure Functional Programming

Really, you can't. Any meaningful program would have at least some output and that violates the rule about a function not having any side effects.

But of course that doesn't stop people from writing them. And here comes the introductory quote from Lu Tze into play: it's still considered pure functional programming if you break the rules, as long as you break them in the right way.

Haskell is considered a "purely functional language" but even Haskell covers input and output. So what do they do?

In short, Haskell introduces its own rules regarding the breaking of the rules of purely functional programming, and then makes the compiler enforce these rules. And that's why Haskell is considered a purely functional language and Common Lisp is not.

Purely function programming shapes the way you are thinking

Since functions must not have side-effects, and always return the same result for the same input, you have to think about how to handle things like state, and where you handle changes to that state. We are talking architecture here, and the same application written in purely functional programming will look completely different when written in object oriented programming.

And that is perhaps the most profound benefit you might get from functional programming: a different, and sometimes far less convoluted, architecture.

Apart from programs that are far easier to test for correctness, that is.