Skip to content

The Rise of Functional Programming

Abstract

Functional programming is one of the oldest ideas in computer science, and one of the longest ignored. While imperative and object-oriented thinking dominated the industry, researchers and purists worked on a different vision: programs as mathematical functions, without side effects, without shared state. For decades that counted as academic. Then came the parallelization crisis, and suddenly the industry had an urgent problem for which functional concepts were the answer.

The Mathematical Root: Lambda Calculus and Lisp

Functional programming does not begin with a programming language. It begins in 1936 with Alonzo Church’s lambda calculus, a mathematical formalism that describes computation as function application. At the same time, Alan Turing developed his Turing machine model. The two models are equivalent: Church and Turing showed independently that lambda calculus and the Turing machine can express the same class of computations.

Lisp (John McCarthy, MIT, 1958) was the first practical realization of the lambda calculus. For Lisp, McCarthy invented garbage collection and the recursive function call as a control structure. Lisp functions were first-class citizens: they could be passed as arguments, returned as values, stored in data structures. In 1958 that was radical.

Lisp survived six decades as a research language, an AI language, an Emacs scripting language. It spawned successors: Scheme (1975), Common Lisp (1984), Clojure (2007). But it never dominated industry.

Haskell: The Ideal of Purity

In 1987, a group of researchers decided that the proliferation of incompatible lazy functional languages was inefficient. The result: Haskell, named after the logician Haskell B. Curry. Version 1.0 appeared in 1990.

Haskell is uncompromising. Pure means: no side effects. A function given the same input always returns the same output. No global variables, no I/O except through explicitly managed monads. Lazy evaluation: expressions are computed only when their value is actually needed. A strong static type system with type inference: the compiler knows the type of every expression, even if the developer never writes it down.

Haskell’s type system became the reference point for language design. Concepts like type classes (Haskell’s answer to polymorphism), monads (for handling effects in pure code), and parametric polymorphism influenced Java, Scala, Rust, Swift, C#, and almost every newer language.

The paradox: Haskell is extremely influential and has barely any industry market share. Facebook used Haskell for its spam filtering system (Sigma). GitHub used Haskell for certain internal tools. Standard Chartered writes its risk management in Haskell. Those are exceptions, not the rule.

Why? Haskell’s learning curve is steep. Monads are notoriously hard to explain; the running-gag definition: “A monad is a monoid in the category of endofunctors. What’s the problem?” That is not a joke. It is the truth. The cognitive overhead keeps many developers away.

Erlang: Functional out of Necessity

Erlang did not arise from an academic love of purity. It arose from a concrete engineering problem.

In 1986, Joe Armstrong at Ericsson began developing a new programming language for telecommunications systems. The requirements were extreme: 99.9999999% availability (less than 31 milliseconds of downtime per year), hot code reloading (updating running systems without stopping them), thousands of simultaneous connections per node.

Erlang solved this with three concepts:

Lightweight processes: not OS threads, but Erlang’s own processes with a few kilobytes of overhead. An Erlang system can run millions of processes simultaneously.

Shared-nothing architecture: processes share no memory. They communicate exclusively through message passing. That eliminates race conditions structurally: there is no shared state that could become inconsistent.

“Let it crash”: Erlang’s philosophy advocates supervision trees rather than defensive programming: when a process crashes, a supervisor starts a new one. Fault tolerance through isolation, not through prevention.

The result: AXD301, Ericsson’s Erlang-based ATM switch, achieved 99.9999999% availability in tests. WhatsApp, built on Erlang (ejabberd), handled about 50 billion messages a day in 2014 with roughly 50 engineers. Elixir (2011), a modern language on the Erlang VM (BEAM), brought these concepts to a new generation of developers with Rails-like ergonomics.

Clojure: Lisp for the JVM Generation

Rich Hickey had had enough. In 2007 he released Clojure (a Lisp dialect for the Java Virtual Machine) with a clear message: object-oriented programming with mutable state is the root of concurrency problems.

Abstract

Hickey’s diagnosis of the OOP crisis: we build objects with mutable state, send threads across them, and then try to contain the damage with locks; the concurrency bugs follow from the design. His alternative: immutable data structures as the default. If data is immutable, there are no race conditions. Multiple threads can read the same data structure; there are no write operations that could endanger readers.

Clojure’s core concepts became more influential than the language itself:

Persistent data structures: immutable data structures that use structural sharing. A modified version shares most of its data with the original without copying it. Efficient and safe.

Software Transactional Memory (STM): coordinated state changes like database transactions, without manual locks.

Clojure remained a niche language, but its ideas were copied. Immutability by default is found today in Rust, in Kotlin, in Swift. Facebook’s Immutable.js brought persistent data structures to JavaScript. Redux (Elm-inspired, 2015) structured React applications along functional principles and became the industry standard for frontend state management.

The Quiet Infiltration: FP Concepts in Mainstream Languages

No mainstream language became functional. But all mainstream languages became more functional.

Java 8 (2014) introduced lambda expressions and the Streams API, after two decades without syntactic support for functions as first-class citizens. list.stream().filter(x -> x > 0).map(x -> x * 2).collect(...) is functional style, arrived in the imperative world.

Python had map, filter, reduce, and list comprehensions early on. JavaScript received Array.map, Array.filter, Array.reduce with ECMAScript 5 (2009). C# 3.0 (2007) brought LINQ (Language Integrated Query), which enabled functional queries over arbitrary data sources.

Rust (2010, Mozilla) made immutability the default: variables are let (immutable) unless declared let mut, an explicit opt-in to mutability. The ownership system eliminates race conditions statically.

None of these languages calls itself functional. All absorbed functional concepts, because the parallelization crisis of the multi-core era created concrete problems for which FP had concrete solutions.

Dead End: Pure FP Languages in Industry

Info

Why Haskell never broke through, and what that says about language adoption:

Haskell’s theoretical superiority in certain domains is undisputed. Programs that compile in Haskell carry an unusually strong correctness guarantee from the type system. Runtime errors that are common cases in other languages become compile-time errors in Haskell.

Haskell nevertheless remained a fringe phenomenon. The reasons are instructive:

The hiring market: Companies hire what candidates know. Candidates learn what companies use. Haskell never broke this cycle at the mainstream level.

Cognitive overhead: Monads, type classes, lazy evaluation, category theory as the explanatory frame: it accumulates into an entry barrier that for most developers is higher than the perceived benefit.

Tooling maturity: GHC (the Haskell compiler) is excellent. The build system Cabal was a well-known pain point for a long time. Haskell IDEs never reached the integration quality of JVM tools.

The actual legacy: Haskell is the Labrador question of programming languages: everyone knows it, everyone respects it, hardly anyone breeds it as a working animal. Its ideas live on in the type systems of younger languages that combine practical ergonomics with theoretical solidity.

Legacy

Functional programming did not win, and won anyway. The pure languages dominate no industry. But no serious language design of the last twenty years ignores functional concepts.

The real shift was conceptual: the industry accepted that mutable shared state is the main source of parallelization bugs. That immutability is not an academic ideal but practical engineering. That functions as first-class citizens make code more expressive.

Haskell said this in 1990. The industry listened in 2010, and translated it into languages it already knew.

πŸ“š Sources