I had a wonderful realization today about functional programming in LISP-style languages (like Clojure). You see, I've been trying to 'grok' functional programming for several years now. It's been a slow, difficult journey. To me, on the outside looking in, everything seemed harder in a functional paradigm.
The traditional ways to express the reasons for the difficulty include ideas like:
- You don't just update the value of a variable to save your place in an algorithm anymore.
- You don't pass references around expecting functions to update them.
- There is no mutable 'state'!
Well, here's a new way to express it that I've never heard before:
In LISP (and it's derivitaves) everything you write is a 'one-liner'.
Have you ever challenged yourself to write an entire routine or algorithm as a single statement or expression? In more mainstream languages this is usually quite a challenge. Why is that? Because you have to think in a functional paradigm! When composing a one-line solution all of the above bullet points apply!
Well, guess what: every top-level expression you write in a LISPy language is a one-liner! Don't believe me? Let's consider a function I wrote earlier today.
For context, the function takes a single string that looks like a square grid with dashes and x
characters:
(str "-x-"
"-x-"
"x-x"
...and returns a set
containing the [x y]
coordinates of the x
characters (assuming that the first character is [0 0]):
#{[1 0]
[1 1]
[0 2]
[2 2]}
Here's what the function looked like:
(defn parse-grid [raw]
(let [width (int (Math/sqrt (count raw)))]
(set (for [n (range (count raw))
:let [x (int (mod n width))
y (int (/ n width))]
:when (= (nth raw n) \x)]
[x y]))))
Notice that the defn
form is a single expression, with a bunch of nested expressions[1], and could therefore be rendered a one-liner:
(defn parse-grid [raw] (let [width (int (Math/sqrt (count raw)))] (set (for [n (range (count raw)) :let [x (int (mod n width)) y (int (/ n width))] :when (= (nth raw n) \x)] [x y]))))
While we do have some great tools for working with these kind of nested expressions, it doesn't change the fact that we are working with some potentially complicated nested expressions to get work done.
[1]The list comprehension employed in this example actually makes this code much more readable than it would be otherwise.