Summary: Lambda abstractions are always leaky, but some are leakier than others. Clojure programmers recommend keeping most of your functions pure and containing the leaks as much as possible.
Lambda abstraction refers to something we do all of the time. Let’s say I have some code:
(+ 1 2)
I’m adding the number 2 to a number, in this case, 1. I could abstract that into a lambda:
(defn add2 [x] (+ x 2))
Now it’s a function, which I can apply to 1.
(add2 1). I can apply it to any number I want. The actual thing I am adding 2 to is abstracted away and replaced by the variable
x. Lambda abstractions are just functions.
Functional programming is at its best when lambda abstractions are referentially transparent. That means that given the same arguments, a function will always return the same value. Being referentially transparent makes a software function more like a mathematical function. And that lets you reason about your code.
But there’s a very real difference between software functions and mathematical functions: mathematical functions take no time or energy to “compute”. They are defined abstractly, with no notion of computation. In contrast, software functions always take some time to compute. Sometimes the clearest way to write a function takes enough time that the illusion of mathematical functions is shattered. The abstraction is leaky.
So software functions are already a leaky abstraction, even if they are referentially transparent. Clojure (like most programming languages) opens the leak even further: you can put stuff that’s not referentially transparent right in your function. For instance, you can write a “function” that reads from the disk or makes a web request. Making the same request twice can obviously return different values.
What most people programming Clojure recommend is to program mostly with pure functions (that means referentially transparent). You still have to deal with time, but that’s way easier than dealing with the chaos of the world outside. That leaves a small bit of your code to deal with mutation, input/output, and the disk. It’s still a lambda abstraction (function) but it’s just leakier. Clojure simply leaves the decision up to you where to draw the line. Clojure tries to make pure functions easy, even when not everything fits into pure functions.
The takeaway of functional programming is the same recommendation: write most of your code as referentially transparent functions. The degree to which a language helps you do that is how “functional” the language is.
If you’d like to learn more about Clojure and pure functions, check out LispCast Introduction to Clojure. It’s 2.5 hours of high quality video. You probably haven’t seen anything like this! There’s animations, exercises, characters, and screencasts. It takes you from no knowledge to a deep experience, all while having fun!