Lessons learned from solving 4Clojure problems

29 December 2012

A few days ago I completed the last problem on the 4Clojure site. If you
want to learn Clojure, solving these problems is a great way to do it. Several
of its features -that I’ll highlight below- make it a great learning tool. Other
features probably arise from Clojure being a
(pragmatic) functional programming language. Coming from mostly an OO background
these were also new to me and thus deserve their own paragraph.

I hope that after reading through the list you’ll end up being persuaded of the
merits and want to solve (some of) the problems yourself. If you do, please let
me know how it went and what you learned from it.

Why is 4Clojure a great learning tool?

Looking at others’ solutions

After solving a problem, you can check how the users you follow solved it.
That’s arguably the most important feature when it comes to learning since it
is essentially a code reading exercise when the functionality of the code is
well-known (since it solves the same problem you’ve solved) and the authors are
probably more proficient.

On several occasions I saw solutions that were both more concise and clearer
than mine (especially when tackling a hard problem). Dealing with the
inferiority complex on the very short term is dwarved by how much wisdom you
gain from these. For what it’s worth, the users I learned most from are
hypirion,
jafingerhut and
chouser.

Executable, well thought-out test cases

To submit your solution, you paste your code into a textbox and click a button.
The test cases, which are visible, are then checked one by one. If all the
lamps become green, your solution is accepted. If not, you get an error
message and have to try again.

This method has several advantages. First of all, it eliminates the imprecisions
you might have had after reading the description of the problem. Second, it
gives you a set of examples to work against. Third, they force you to think
deeper about the problem since they are constructed to reject a partial
solution.

Timeouts

Your solution can be functional and pass all the test cases but if it does not
finish in a certain time, it will get rejected. I bumped into this on several
occasions. Most of the time it was because I came up with the brute force
solution to a hard problem and hoped I’d get away with it. In other cases it was
because of a technical issue, like allocating too much memory.

In the first case, it made me think again about the problem (see Hammock-Driven
Development) and come up with a more ingenious solution. In the second case, I
learned something about a technical aspect of the language. In both cases, I
grew a bit wiser about optimization -which is a “real-world” coding skill- so
I’m happy the authors of 4clojure chose to implement this constraint.

What does one learn about (functional) programming?

Hammock-Driven Development

Also known as “step-away from the computer to solve hard problems”, Hammock-Driven
Development is a term coined by Rich Hickey, the creator of Clojure, in a keynote
speech.

Apparently ridiculing Test Driven Development (TDD), HDD holds that the most
important activity to solve a hard problem is to think deeply about it without
any distractions. Most of the time sitting in front of the computer is a
distraction in itself since it begs to be typed on and prevents actual deep thinking
to happen.

This one is really hard to get used to because whenever we write code we feel like
we’re getting closer to a solution. Thinking, on the other hand, does not
provide any tangible output.

However, HDD has rung ever more true with me as I progressed. When tackling hard
problems, I tended to think about them for some time and then started to type in
actual code. The problem was, when I felt that the solution became convoluted I
did not go back to the proverbial hammock but carried on with the implementation.
Most of the time it either turned out to be a dead-end or a solution I’d much
better hide.

Even more importantly, a cleaner solution is one that is easier for
others to understand. Since code is mainly for others to read and occasionally
for computers to execute, more thinking up-front results in less time spent
developing and maintaining the code down the line.

The REPL is a powerful developer tool

The power of the REPL is one those realizations most of us coming from OO will
come to. Since FP languages have very little state and side-effects and
thus a lot of idempotency, trying things at the REPL is taken to the next
level. You launch a REPL once and then copy-paste the building blocks of your
solution between your editor and the REPL (and there are bettersolutions then copy-pasting).

Use higher level functions

FP languages strive to have a small set of data structures and a high number of
functions that operate on them. Clojure is no exception. Though it’s not hard to
assemble the higher-level function you need yourself, in the majority of cases,
it’s just extra work: it’s probably already defined in the core.

This is something that I learned by reading others’ solutions and learning
about awesome functions (frequencies, merge-with, condp come to mind).
After a while I looked up the high-level function myself from the
cheatsheet.