Clojure Tip 💡
Over the weekend, I rewatched Clojure, Made Simple, Rich Hickey’s 2015 talk at Java One.
In it, Rich made this claim about a Clojure translation of some Python code:
It is as short as the Python program. And that is not like a contest. It is more about this: Everything that is in here is about the problem. All the words you are reading, and everything, it is all about the problem. There is no extra stuff. There is no
static import, blah, type this, type that, yada yada, extra control stuff. It is all about the problem. 100% about the problem.
That got me thinking: What does it mean for code to “be about the problem?” In the context of the talk, he’s comparing Clojure to Java. He is saying that Java is verbose and that a lot of what you have to say has nothing to do with the problem. But is Clojure’s code “100% about the problem?” It is odd to me when your domain is spelling correction, yet only a tiny fraction of the tokens in your code refer to concepts in that domain.
I believe Rich was saying that 0% of the code is ceremony. In Java, you might have to create a new file, define a class in it, give it a constructor, define a method. Then you construct the instance and assign it to a variable (with declared type). Finally, you can call the method on the object. That’s a lot of ceremony to get a named, useful thing. In Clojure, you define a function. Then you call it. In Clojure, ceremony is near zero.
However, you’re still writing a lot of code that doesn’t directly address the statement of the problem. Instead, much of it is about the details of the solution. For example, the code defines four kinds of edits: character deletions, character swaps, character insertions, and character replacements. These are each calculated using for comprehensions, and the code is quite tidy. But it defines the edits in terms of substrings and string concatenation, which is not part of the domain, and it never names the edits.
How would you make the code more about the problem domain? Well, you could define a new kind of thing, call it a
Word. And you could define a routine on
Word that returns a set of
Words that are one character deletion away. And another routine that returns the character swaps, etc. Then you could define a routine called
edits that adds all of those together.
You can see where this is going. In Java, you would define a class called
Word with methods
edits(), etc. Now you’re able to speak in domain terms all the time. You could do the same with
Dictionary, which contains all the known words, with methods like
Now, of course, you’re back where you started: You’re writing ceremonial code instead of code that solves the problem. If this indeed is a problem, there are two possible root causes:
- Code defining the domain is always a problem.
- Code defining the domain might be good, but in Java, it’s just too verbose.
I can’t believe that defining the domain is always bad. Is the problem that Java is verbose? What if you could write a new class with a couple of methods as concisely as writing a couple of functions in Clojure? Would you get the best of both worlds? One of the problems that Clojurists report (and I have experienced firsthand) is that assumptions go unstated in Clojure code. As the code grows, so do the implicit assumptions. At least the Java code makes some of those explicit. Can we imagine a different OO language that got right to the point? Would we still have the same complaints?
At this point, I should mention that I’m reading a book about Smalltalk called Smalltalk Best Practice Patterns. Smalltalk is very concise. It’s nothing like Java. It makes me ask again: “Is Smalltalk a functional language?” The book is good and I like what I see of the language.
However, I think there is an argument to not going overboard trying to define a bunch of domain terms first. Inline is usually better. There are two main reasons:
- Less code is better.
- The direct code mindset is better.
Less code means fewer bugs. Various studies show this (and explicitly stated in Rich’s talk). And this tip is already too long. We’ll have to address #2 next time.
This Thursday is the Full Strawberry Supermoon. The moon is close and may appear reddish in color.
I decided to change the name of my major offerings. What I was calling The Beginning Clojure Video Course Bundle, I’m now calling Beginner Clojure: An Eric Normand Signature Course. Along with the name change, I’m going to raise the price this Friday. The old price is still available until then. Get it now while you can!
Beginner Clojure: An Eric Normand Signature Course is meant to teach the mindset and practice of Clojure as you would use at a job.
Awesome book 📖
In typical Sussman style, the prose is tight and clear. I have been enjoying the explanations of why one would choose each particular technique. It’s worth the price just for that.
The documented techniques are powerful. Although they are not new, the presentation in a single volume is. I’m glad that pattern matching, unification, continuation-based parsing, etc, which I learned mostly in posts on Usenet, are now published accessibly.
The exercises are quite challenging. It is a textbook meant to be used in class, presumably with the exercises given as homework and worked out during class. For some of the problems, I would have rather had them document the techniques. But it’s also a big book already.
I’m still trying to wrap my head around the use of the term software design. I have a complicated relationship with the term already. This book only complicates it further. Others have used design to mean “decisions that decrease the cost of maintenance”. This book is no different.
What is different is that “build a backtracking rules engine” was never on the table in other books. It was always about where a method lives or what your
isA hierarchy looked like.
For its intended audience, the book is spot on. I hope that enough people stretch themselves and work through it that we figure them out and popularize them. This book feels like a toadstool popping out of the underground Lisp mycelium network. It’s an old network, thriving as it always has, and this book is there to spread the spores for a new generation. A lot of the wisdom of Lisp is in the network. The nodes in the net are people. Unlike a fungus, which can live for thousands of years, people die. This knowledge needs to get out and spread wide before the nodes blink out.
Like I said before, I was familiar with a small number of these techniques from the early 2000s trolling
comp.lang.lisp. I knew there was more, but how to find it? This book captures a lot of the Lisp techniques I thought would die out. If any of that appeals to you, get it. Put it next to SICP (same cover style) and work through it slowly. Savor and enjoy.
Book update 📘
Grokking Simplicity is now available on Amazon! People have already been receiving their copies.
Please, if you like the book and/or believe in its mission of starting a discussion about the practice of FP in the industry, please leave a 5-star review. Reviews will help people learn whether the book is good before they buy. I’m up to four reviews so far.
You can also get a copy of Grokking Simplicity at Manning’s site. There you can use the coupon TSSIMPLICITY for 50% off.
Quarantine update 😷
I know a lot of people are going through tougher times than I am. If you, for any reason, can’t afford my courses, and you think the courses will help you, please hit reply and I will set you up. It’s a small gesture I can make, but it might help.
I don’t want to shame you or anybody that we should be using this time to work on our skills. The number one priority is your health and safety. I know I haven’t been able to work very much, let alone learn some new skill. But if learning Clojure is important to you, and you can’t afford it, just hit reply and I’ll set you up. Keeping busy can keep us sane.
Stay healthy. Wash your hands. Wear a mask. Take care of loved ones.
Clojure Challenge 🤔
Last issue’s challenge
This week’s challenge
Balanced Paren Clusters
Given a string of balanced parentheses, split it up into top-level “clusters”.
(clusters "") ;=>  (clusters "()") ;=> ["()"] (clusters "(())") ;=> ["(())"] (clusters "()()()") ;=> ["()" "()" "()"] (clusters "(()())()(())") ;=> ["(()())" "()" "(())"]
You can assume the strings will only contain
\) and will be fully balanced.
- Handle unbalanced parens with a nice error message showing the relevant positions.
- Handle other braces, such as
Thanks to this site for the problem idea, where it is rated Very Hard in Python. The problem has been modified.
Please submit your solutions as comments on this gist.