Functional Programming: The Good Parts

In a famous paper published in 1998, Philip Wadler complained that no one used functional programming languages. It is safe to say that in 2026 everybody is using some kind of functional programming language, albeit to a certain extent, but the underlying reason for this spread had more to do with fashion and hype rather than market economics or academic support.

“Modern” programming languages include, almost without exception, features borrowed from the realm of functional programming languages. This can be briefly summarized as follows: “lambdas” or “closures” that can be passed around and called a piacere; a series of functions to manipulate arrays called map(), filter(), and reduce() (provided either as standalone functions or as methods of some Array class provided by the languages’ runtime libraries); immutable records or classes, guaranteed to be unchanged throughout the execution of the program; and sometimes even a “pipe operator” looking like this: |>.

In the latter case, we can include the humble PHP programming language, which features such a contraption since version 8.5, released last November at the time of this writing. Which means that the two statements below produce similar results:

$result1 = trim(str_shuffle(strtoupper("hello")));
$result2 = strtoupper("hello") |> 'str_shuffle' |> trim(...);

Spoiler alert: the ellipsis (since PHP 8.1) makes a closure out of a pre-existing function, but as shown above, you can just use a string with the name of the function you would like to “pipe” your output into. Oh, and beware of the fact that array_map() takes the callback function as the first argument rather than the second, which means that… you need a wrapper to invert the arguments. Yeah, as one author describes the current situation in PHP,

The deeper issue is the stdlib itself. PHP’s stdlib was never shaped for chaining. array_map and array_filter taking arguments in different orders is a 1995 design that calcified before anyone thought about composition. The pipe operator works around the symptom. Native methods on arrays and strings would fix the cause.

(Takes a deep breath.)

Other than such small shenanigans, and thanks to the introduction of the pipe operator, recently laid-off developers coming from the F#, Elixir, OCaml, Elm, or Julia galaxies should now feel at home with PHP (well, almost). Note to Haskell developers struggling to pay rent and willing to jump on the very lucrative market of PHP: in your case the pipe operator looks like an &. You have been warned. Oh, and JavaScript developers will have to wait for their own pipe operator, but it might happen sooner than expected.

Let us be even more radical: according to Kevlin Henney, Excel is the world’s most popular functional programming language, and even better, it now comes bundled with a LAMBDA() function. How about that?

Are these features enough to make PHP (or Excel) a real functional programming language? Let us recap. Closures? Check. Map, filter, and reduce functions? Check. Readonly classes? Check. Pipe operator? As we have seen above, check.

But of course, no, PHP is not really a pure functional programming language. It still allows for side effects, and that is a big no-no-no in that world. Even worse, it is quite obviously a procedural language, not really a declarative one. But with a little bit of attention, and maybe with the help of your preferred LLM-powered IDE and some unit tests, your PHP code can reach levels of “functional compliance” Rasmus Lerdorf could only dream of (well, perhaps he never dreamt of such a thing; I give you that).

The real question is the following: why does a parochial programming language such as PHP now include these features? The reasons behind the choice to add them as part of the specification have a strong element of hype.

The jump to concurrency and the economics of cloud computing have given functional programming constructs a boost that neither desktop nor mainframe applications ever could. Let us be honest here: in the world of concurrency, multicore CPUs, at the end of the “free lunch” world, functional languages shine.

The trend was apparent at the beginning of the 2000s, and it simply exploded during the 2010s. Scala was released in 2004, becoming the first functional programming language built on top of the JVM; standard Java developers would have to wait until Java 8 (released in 2014) to be able to write code including lambda expressions and its associated map, filter, and reduce functions. On the other side, the C# community discovered LINQ in 2005 already, effectively transforming array manipulation into a SQL-like contraption. In the galaxy of Apple, Objective-C blocks and their associated arcane syntax appeared together with Grand Central Dispatch around 2009. C++ debuted lambda expressions in its specification in 2011.

Languages created in the past 20 years almost inevitably include one or another of the functional programming features enumerated above: Go functions are first-class objects. TypeScript builds on top of the already existing JavaScript language. Rust closures come in various “traits” depending on how they capture their surrounding environment. Elixir brought closures on top of the Erlang BEAM. Dart has supported closures since its first version. F# brought rather pure functional programming features to the .NET runtime. Swift had first-class functions from day one, and for a while, even object-oriented methods were implemented as closures. Finally, Kotlin brought a shorter, simpler syntax for closures on top of the JVM.

Oh, and then PHP followed the trend, obviously. By the way, it is worth mentioning that Python, Perl, and Lua have had support for lambdas since the 1990s, at a time when these concepts were still considered fringe.

Undeniably, in the same vein, the rise in popularity of Python, Ruby, and JavaScript during the past two decades also contributed to the slow march of functional thinking into the minds of software developers worldwide. Speaking (again) about JavaScript, we have talked about Douglas Crockford’s magnum opus in a previous article of this magazine, and it was obvious to him that one of the “good” parts of JavaScript was, precisely, its functional programming roots:

JavaScript is built on some very good ideas and a few very bad ones.

The very good ideas include functions, loose typing, dynamic objects, and an expressive object literal notation. The bad ideas include a programming model based on global variables.

JavaScript’s functions are first class objects with (mostly) lexical scoping. JavaScript is the first lambda language to go mainstream. Deep down, JavaScript has more in common with Lisp and Scheme than with Java. It is Lisp in C’s clothing. This makes JavaScript a remarkably powerful language.

The quote above explains why, at the time of this writing, the latest version of the quintessential book on functional programming, “Structure and Interpretation of Computer Programs”, by Abelson and Sussman, is the “JavaScript Edition”. The choice of JavaScript somehow gives reason to Philip Wadler, who in 1987 argued that using Scheme was probably not the best idea to begin with.

Lisp would certainly deserve a whole issue of this magazine, but chapter 5 of Waldrop’s “The Dream Machine”, a book we reviewed precisely a year ago, contains a beautiful description of its early history and its impact:

Nonetheless, it’s fair to say that one of Lisp’s two greatest legacies to the art of programming was a certain style, a certain exploratory approach to pushing back the software frontiers.

And the other legacy? An undeniable grace, beauty, and power. As a Lisp programmer contined to link simpler functions in to more complex ones, he or she would eventually reach a point inwhere the whole program was a function–which, of course, would also be just another list. So to execute that program, the programmer would simply give a command for the list to evaluate itself in the context of all the definitions that had gone before. And in a truly spectacular exercise in self-reference, it would do precisely that. In effect, such a list provided the purest possible embodiment of John von Neumann’s original conception of a stored program: it was both data and executable code, at one and the same time.

Since its presentation in a now legendary paper by John McCarthy, Lisp’s power and beauty have often been mentioned by pundits and entrepreneurs alike. In the latter group, we cannot avoid mentioning Paul Graham: his 2001 article “Beating the Averages” tells the story of his 1995 e-commerce startup “Viaweb”, built in Lisp, a language he deeply loved. That article also makes a surprising connection between Lisp and the (badly named) “Sapir-Whorf hypothesis” of linguistic relativity:

By induction, the only programmers in a position to see all the differences in power between the various languages are those who understand the most powerful one. (This is probably what Eric Raymond meant about Lisp making you a better programmer.) You can’t trust the opinions of the others, because of the Blub paradox: they’re satisfied with whatever language they happen to use, because it dictates the way they think about programs.

Paul Graham was not the first to make the connection to the Sapir-Whorf hypothesis, by the way: Kenneth Iverson, creator of the APL programming language, also explained this during his 1980 Turing Award lecture, “Notation as a Tool of Thought” as follows:

Concerning language, George Boole in his Laws of Thought asserted “That language is an instrument of human reason,, and not merely a medium for the expression of thought, is a truth generally admitted.”

Mathematical notation provides perhaps the best-known and best-developed example of language used consciously as a tool of thought.

Those of us brave enough to have written code in APL can only agree that its notation brings a whole new meaning to the word “programming”.

So it was that, standing on the shoulder of this giant called Lisp, people came up with projects ranging from Medley Interlisp to a Lisp interpreter built as Conway’s Game of Life.

Lisp, and later functional programming languages, have enlightened software programmers to go above and beyond what commercial offerings could even dream of. They have stretched our thinking, opening doors to concurrency, mathematical breakthroughs, early artificial intelligence efforts, and the widest possible array of scientific exploration about computation.

Yet, already in the 1990s it was apparent that Lisp (and, to a large extent, functional programming) was at risk of being relegated or even of becoming extinct, and today some influencers ask themselves, where did all the functional programmers go? The answer is simple: they are all among us; we are all, to a small or large degree, better software engineers because there has been a healthy breeding process between procedural, object-oriented, and functional programming languages that begat modern tools for a modern world.

All things considered, the most important contribution of functional languages in the modern world was a certain way of thinking about programming, more than just actually adopting “pure” functional compilers in our day-to-day jobs. Put in other words, mastering functional programming concepts fundamentally transformed the way we create software. And in the best possible way.

Cover photo by Pablo Martinez on Unsplash.

Back to top