Clojure: the good, the bad and the ugly

Four years ago I wrote about my first (and last) experience with Common Lisp. I had high expectations and was disappointed; I ended up thinking maybe I should give Scheme or Clojure a try. It took a while, but I finally did it last month: learn Clojure. And it looks like a keeper.

Clojure has all the goodness of Lisp and functional programming, and it feels like a modern language: it addresses most of things that annoyed me about Common Lisp.

I’ve followed the great Clojure for the brave and true book by Daniel Higginbotham and then I’ve tackled a small project to train my skills. Here are my notes.

The good

  • A consistent syntax, operation names and polymorphic functions. No weird illegible names, no type specific versions of the same function (I’m looking at you Common Lisp).
  • Functional! Immutable! Expressive!
    • I don’t miss objects. Structuring programs in small functions; isolated, never changing data; those things just feel right. And I’m not even waving the old “shared state is bad for concurrency” flag; I don’t care —just now— about concurrency. This stuff makes programs simpler to reason about and test, and more fun to write.
    • There’s more: Clojure is so expressive and gives you enough options (I’m thinking loop, doseq, destructuring, etc.) that you don’t necessarily need to incur in “head/tail” recursive processing as much as I found in other functional languages, so the leap is not so rough.
  • Did I say I don’t care about concurrency? I don’t. Mostly. Not at the language level, anyway. Clojure has a lot of cool tools for concurrency (future, delay, promise, pmap, core.async). Too much options, maybe, but I don’t mind about that either. I can just RTFM whenever I do have the need to do things concurrently. And, yes, immutability and pure functions make it simpler.
  • A strong philosophy behind the language, that seems to drive its design. Python has this too and to me it’s its biggest selling point. Languages like C++ and increasingly JavaScript, on the other hand, feel like magic bags where features are added carelessly without consideration of the results. Java does have a strong philosophy: programmers are mostly idiots.
  • Runs on the JVM. Seriously? I personally couldn’t care less about that but JVM languages seem to attract a lot of attention. There are tons of Java devs for sure and some of them seem to have a symbiotic relationship with the JVM: it’s like they aren’t cheating on Java if they keep the deal inside the VM. Why would people learn Groovy instead of Ruby or Python, god only knows, but because of the “Runs in Java” part there’s a better chance of finding a Clojure Job than one using Haskell, Scheme or most other functional languages around. That alone is enough reason for me to stick with Clojure instead of keep trying Lisp dialects, even if some other one may fit my taste better.
  • Haven’t tried it yet, but the mere existence of ClojureScript is good news to me, specially considering how annoyed I am with the direction the JavaScript syntax and ecosystem is taking lately. This talk totally sold it to me.
  • Said it before and say it again: forget about parenthesis. People seem to worry a lot about them beforehand, but as with Python whitespace indentation, once you’ve used it for five minutes it just goes away. Specially if you use the darn awesome Parinfer.
  • Which brings me to: you don’t need Emacs for Lisp programming. Yes, I hear you, once I master Emacs I’ll be a more powerful programmer. But I’m trying to learn a weird language here, don’t make me also learn a weird, counter-intuitive editor at the same time. That would just increase the chances of me dropping the effort altogether. There are decent ports of Paredit for Sublime and Atom, which is good enough. But with Parinfer you just learn one command and forget about it, it just works.
  • REPL driven development. Because of pure functions it’s easy to write a piece of code and test it right away in the REPL. Together with unit tests it pretty much removes the need for debugging.
  • Leiningen looks good, it covers the small needs I had starting out and didn’t get in the way. Clojurians say it does a lot more than that, so great. Much better than the 17 tools you need to set up to have a Node.js project running these days.

The bad

  • Namespace syntax is complicated, there are too many operations and keywords to do it (require, refer, use, alias, import and ns —which can do all of the others with a slightly different notation). It’s flexible but boilerplatish, even when sticking to ns:
(ns advenjure.game
  (:require [advenjure.rooms :as rooms]
            [advenjure.utils :as utils]
            [advenjure.verb-map :refer [find-verb verb-map]]))
  • And while there’s no hard rule to keep a one to one relation between files and namespaces, there’s a strong convention to do it, so having to declare the package name in every file seems totally redundant (and Java-ish, let’s be honest).
  • contains? Works in a counter-intuitive way for vectors.

That’s all I got.

And the ugly

Ok, there wasn’t much bad stuff, but there is some maybe not so good or arguably not good things I can think of.

  • The built in operator set doesn’t follow the Unix and Python philosophy of small core and a lot of libraries that I like so much: the functions are way too many to easily remember, and they aren’t entirely orthogonal (some of them do the same thing in slightly different ways). Then again, the Clojure Cheatsheet, the REPL and doc are more than enough to cope with that.
  • Polymorphism is great: sequence and collection functions work as expected in all data structures. The downside is that to do so the results are always coerced to seqs, which may be unexpected, specially for hash maps. In practice, though, I found myself just chaining those functions and rarely caring about the resulting type.
  • Macros are powerful and awesome but the quoting syntax can get very tricky. I definitely need more experience to learn to reason about macro code, but the syntax will remain ugly. I guess that’s the cost you pay for being able to fiddle with how the language processes the code. In the end (much like Python metaclasses), macros are a great tool to keep in the box, but to use sparingly. So far every time I thought about implementing one I got away fine by using closures instead.
  • Java does sneak in quite a bit and that’s a turn off. (spoiler alert: I don’t like Java). OK, Java interop is simple and powerful, probably the most straightforward language interfacing I’ve seen (boy was SWIG a nightmare). That being said, Java code inside Clojure looks like, well, Java code inside Clojure: it reeks. This wouldn’t be so much of a problem if needed only to interact with some third party Java libraries, but in practice I’ve found that there’s basic stuff lacking in the Clojure standard library and it’s either add a dependency or use Java interop. I saw this while solving an exercise from the Brave Clojure book: it asked to list the first results of a google search. The request should be a one liner using the built in slurp function but, wait, you need to set the User-Agent header to request google, so you end up with:
(with-open [inputstream (-> (java.net.URL. url)
                            .openConnection
                            (doto (.setRequestProperty "User-Agent"
                                                       "Mozilla/5.0 ..."))
                            .getContent)]

The end

Even though it’s not my ideal language and it may be less ideal to me than Python was, it looks like I’ll start to look for excuses to use Clojure as much as possible and it’ll be a while before I jump to study another new language.

Python doesn’t treat me like I’m stupid

I don’t know how it works worldwide, but here in Argentina, most programmers not only use Java, but are also convinced that Java is the best language available if you can do without C/C++ efficiency, and therefore don’t bother to learn other languages. There’s no such concept as the “programmer toolbox”. And this is not enforced only by the Software Industry (where non technical issues, like programmer availability come to play[1]), but sadly by a noticeable part of the academic community.

There’s a fact that I think makes this situation worse, and is that Java is a pretty dated language. What I mean is there are too different problems;  The first one is that programmers don’t bother to study other languages, so they use one tool to solve all problems. This is indeed a big issue, but more related to engineering and science than to programming. The second one is that there clearly are better languages to do what Java is supposed to do best: general purpose object oriented programming, and if you may, web applications back-end programming (which is what most programmers end up doing here). And those better languages are not obscure novelties as some like to suggest, but languages that have been around as long as Java has. Ruby and Python are the examples I’m most familiar with, but there are others, notably lisp.

I don’t know about any Java programmer that having tried a higher level language such as Python, wanted to go back to using Java. Bruce Eckel who wrote the books that are used for reference on Java and C++ in the programming courses that teach object-oriented programming at my college  noticed almost 10 years ago that sticking to Java was a huge waste of programmer’s time. A lot has been written about the advantages of Python over Java; a specially concise example are Eckel’s slides Why I love Python[2]. I also like a couple of screencasts by Sean Kelly, that focus more on web development (where Java is specially useless)[3].

I’m not interested in repeating all those points; most of them are related to language features like dynamic typing or native types. Some of them can be overcome by enhancing the language with new features, and this is what Java designers have been doing: they have been patching bad language design decisions ever since it first came out.

But there’s one particular aspect that I think defines on principle why Python is a better language than Java, and it won’t change no matter what features are added or removed. And it is the philosophy behind the language, the way they think about the programmer. Quoting Eckel: Java treats programmers like they are stupid, Python doesn’t.

Java design and libraries consistently make a huge effort making it difficult for a programmer to do bad things. If a feature was a potential means for a dumb programmer to make bad code, they would take away the feature altogether. And what’s worse, at times they even used it in the language implementation but prohibited the programmer to do so[4]. Paul Graham remarkably pointed this[5] as a potential flaw of Java before actually trying the language:

Like the creators of sitcoms or junk food or package tours, Java’s designers were consciously designing a product for people not as smart as them.

But this approach is an illusion; no matter how hard you try, you can’t always keep  bad programmers from writing bad programs. Java is a lousy replacement to the learning of algorithms and good programming practices.

The implications of this way of thinking are not limited to language features. The design decisions of the underlying language have a great effect on the programmer’s idiosyncrasy. It’s common to see Java designs and patterns that make an unnecessary effort in prohibiting potential bad uses of the resulting code, again not trusting the programmer’s judgment, wasting time and putting together very rigid structures.

The bottom line is that by making it hard for stupid programmers to do bad stuff, Java really gets in the way of smart programmers trying to make good programs. Python on the other hand, is focused on making it extremely easy to program well[6]. It gives you the choice; If you want to do crappy code is your responsibility.

[1] Paul Graham, Revenge of the Nerds.
[2] Bruce Eckel, Why I love Python. The points are extended in these interviews.
[3] Sean Kelly, Recovery from addiction, Better web app development.
[4] Bruce Eckel, The Zen of Python.
[5] Paul Graham, Java’s Cover.
[6] Tim Peters, The Zen of Python.

C++ is rubbish: some notes

Again, I’ve been forced to use C++. As in all my previous experiences with the language, it’s a major pain in the ass. I came to the conclusion that C++ is a programming language which no one can ever master; no matter how long you use it, after six months off of it you forget everything you knew, because it’s just too big and completely counterintuitive.

Someday I might take the time to explain more profoundly why I hate (I’m using the word hate here) C++. For now I’ll just make some notes.

I’ve been forced many times to use it, and no one could ever give me a good reason why. I believe such reason does not exist. If you want to do low level, system level or whatever you want to call it programming, C will do. While it lacks the facilities of more recent languages (and lisp), making it a doubtful choice for general purpose programming when efficiency is not the primary concern, I found C to be a really simple and clean language. It’s not at all hard to keep the primary feature set and libraries in your head.

C++ on the other hand is huge. You need to google constantly to do the most simple things, and what’s worse, you should do it, because there are a lot of things that just don’t work in the obvious ways. This is specially true when working with files.

All the advantages of object-orientedness quickly fade away when you see yourself dealing over and over with obscure syntax errors on classes that are a hundred lines long way before they do any real work. The real advantage of object oriented programming is the way it helps to think and structure your programs, and you don’t need classes or inheritance to accomplish that.

C++ is so bad that makes Java look good. I don’t like Java. I think it’s too verbose and lacks several features I expect from a high level language, most of its advanced techniques (design patterns, reflection, xml frameworks) being just patches for those absences. But all in all, Java is a language that makes sense. C++ is not.

It’s backwards compatible with C. Seriously Stroustrup? What on earth were you thinking?

The compiler never helps. The error messages never make sense, and in most cases ignoring them is the healthier choice.

C++ Strings suck. They suck so hard that they’re not a built-in type, they’re not even used in the libraries, so you have to use stuff like c_str().

Streams suck. The syntax is weird, they’re just too many of them, I seem to recall there was some diamond inheritance weirdness going on over here… I’d rather stick to C files.

STL sucks. The iterators are error-prone and tedious to use. You have to write a lot to use most of the methods of the containers, even for basic stuff, ending up with code like:

    for (map<string, string>::iterator it = myMap.begin(); it
            != myMap.end(); it++) {
        string key = it->first;
        string value = it->second;
        //...
    }

Don’t even get me started on templates.

Whether you use them or not, namespaces are a pain in the ass. If you do use them, the code gets confusing and annoying to write (and it’s easier to get compiler errors when you forget to specify the namespace). If you don’t use them in your code you still need to add the using namespace std bit everywhere or you get syntax errors that as usual don’t point the problem.

Why are there references? One can’t consistently use either references or pointers, you are forced to mix them in certain contexts [1].

The language is not consistent. An example: you instantiate classes in the stack like:

    MyClass myObject(arg1, arg2);

But what if your class has a constructor that takes no arguments? You might expect it to work like this:

    MyClass myObject();

But no, the correct way is omitting the parenthesis:

    MyClass myObject;

You might find yourself wondering for half an hour why your program won’t compile for things like this.

Methods are not virtual by default. Premature optimization is the root of all evil.

It’s copy-paste oriented. Code duplication is trouble. In C++, the standard way to code a class is to copy all the method prototypes from the .h to the .cpp file, adding the MyClass:: bit. Even with just a couple of getters and setters, theres a whole lot of typing and copying. This is not only tedious, but one of the places where more errors occur. And remember, the compiler speaks its own language.

You can’t refactor. Unit testing frameworks are awkward to use and setup. Creating a class and making it compile is so hard that most of the time you stick to add an if.

[1] http://www.paploo.net/programming/rant_01.shtml