Hacker News new | ask | show | jobs
by nickbauman 3878 days ago
It is a myth that you have to be a genius programmer to pick up Lisp dialects like Clojure. I think it's the opposite: the cognitive load of more complex languages that are "easier to learn" than Clojure (like Scala or even Java itself, for instance), a complexity I consider accidental and not essential to whatever problem you are solving, is more difficult.

The hard part of Clojure really boils down to one thing: you do not have an assignment operator. If you realize you must now program without that, how would you do that? Answering that question in concrete cases is your only real problem.

Other things, like the parens, well, you stop even noticing them after your first few hours. When you first start, just move the paren one word to the left from where you're used to and you're good to go. The syntax after that is so simple, you will find it liberating.

7 comments

For myself, there are a couple things that make Clojure difficult. I believe they all stem in part from the language being simple.

First, there seems to be at least two ways to do things: the verbose way and the succinct way. This creates more things beginners have to memorize.

Second, so many :keywords. Presumably some function/macro needed to be made more flexible and/or more succinct for certain circumstances so they add magical (from this beginner's POV) :keywords to make it translate their arguments in different ways. Clojure functions seem more like Unix commands rather than functions in most programming languages I'm used to.

And finally, an imperative programmer's tendency to create long functions. As a beginner my brain starts to shutoff once I encounter a Clojure function that reaches 5-10 lines. But 5-10 lines in an imperative language is pretty short.

Clojure is a simpler language, but because of that it doesn't benefit from the syntax highlighting more complex languages may have. This makes it more difficult (for me) to parse similarly lengthed Clojure programs. Of course similarly lengthed Clojure programs can do more than an imperative language, so it should probably be split into more functions, which would make it easier for me, as a beginner, to read.

I feel like people tout the succinctness of Clojure as a benefit because you can write shorter programs. I've come to think it's a benefit because you can section off more of your code into descriptive functions without exploding your code length, which make the program easier to read. When I start to use that succinctness to write less code it quickly becomes unreadable if I revisit it in a month.

Well I think you're absolutely right! There was a post not too long ago about creating "friendlier Clojure" that was all about addressing this problem. I find that I write the code first with pure "intellectual will" until all my tests pass. Then I go back and try to simplify, pull out functions into smaller functions and remove more complex logic.

As far as syntax highlighting is concerned, I find that using emacs helps a lot here. This is my `emacs.d` config.

https://github.com/nickbauman/emacs-dot-dee

This allows structural navigation for Clojure code. Good luck.

> you do not have an assignment operator

Except that you do have assignment. Even more so, every def is a Var, defn definitions themselves being vars that can be redefined, in true Lisp tradition and you also have atoms and you can use mutable arrays or any mutable collections you like, with people doing plenty of that. The emphasis on its simplicity is also misleading. For example Clojure developers pride themselves on how Clojure does not do OOP, except when it does of course, plenty of examples being in Clojure's standard library, starting from really basic things such as ISeq.

And this is actually confusing beginners that read introductions such as yours and I think it's doing Clojure a slight disservice, especially because this isn't defining what Clojure is or explaining why it is awesome.

I do agree with the sentiment. Just the other day I was trying to understand a piece of C# code that was written in a classic style, mutating variables and arrays in place to calculate something that could have been described as pure and very understandable expressions. And oh my god, it's as if I forgot how that was and I hated every minute of it.

Not in any way that matters to people who use the assignment operator in other languages. Of course what you're saying is correct but pointing this out to beginners is like explaining that the steering wheel of the car can be removed and used as a lethal weapon. It's pedantic and misleading.

I don't say and I don't see others saying "Clojure does not do OOP". If you notice this feel free to point this out; even ask me for support. Our community should be exemplary of keeping these things consistent. We all need to do our part.

The tradeoff for anything "simple" is that you are then left to pick up the pieces (with libraries or your own code) if you want to do anything "complex".

As an example: C doesn't have the complication to the language of having "built in" types for hashes or lists. This makes the language easier, since you don't have to learn the extra syntax and grow the mental model of how they were implemented. On the other hand, if you need a hash or list - you need to create your own (or adopt someone else's implementation via a library).

C is a simple and powerful language, but there's so much you have to do yourself, that it's easy to get something wrong.

Clojure (and lisps in general) hide their complexity in macros. This level of abstraction can be great, until you have to dive into the macro and see exactly how it's manipulating your code. Or write your own and troubleshoot the resulting dynamic code.

The tradeoff you're referring to is summarized "Powerful but doesn't provide much." JavaScript comes to mind. This is not the case with Clojure. Most macros are basic and straightforward. Their power is, of course, unparalleled in other languages feature sets. With great power comes great responsibility, eh? And you don't have to use macros. Not that much is implemented in macros. I think you have some valid insights but you may be overstating your case.
> This is not the case with Clojure.

Well, it actually is the case, it's just that they've built out the core library with a ton of functionality; intermingling the actual Clojure keywords (there's only ~17 of them) with the convenience macros and functions built up from those two primitives. Someone wrote all of those functions and macros. Hundreds of functions and macros were built by the language creators to give the functionality of a complex language.

> Not that much is implemented in macros.

74 of the functions exposed in the core api alone are macros. Perhaps the most often used one is "defn" (and somewhat ironically "defmacro").

    intermingling the actual Clojure keywords (there's only ~17 of them) with the convenience macros and functions built up from those two primitives.
It sounds like you are drawing a distinction between the core Clojure language and the macros and functions in the core Clojure namespace. This is not really a valid distinction. That's the thing about Lisps, that much of the language is implemented in the language itself. Macros make the language easier to extend by both the language designer and random developers and users in appropriate cases. You can keep special forms to a minimum. Those those functions and macros are considered part of the core language even if they aren't special forms.
In case you're interested, Lisp can be implemented with 7 primitives (some say 5). So in a sense Clojure went overboard to increase the verbosity of Lisp to make it easier for newcomers: http://stackoverflow.com/questions/3482389/how-many-primitiv...
... so it sounds like we're in violent agreement.
> Not that much is implemented in macros.

And this is exactly what is wrong with Clojure and its community. An unexplainable lack of ability to embrace macros and all the power they can bring.

I could never understand a single anti-macro argument, they are all too detached from the reality.

Is it really lack of ability to embrace, or a desire for simplicity of understanding? (which is also reflected in working with plain data structures in all kinds of libs). I think core.async is a great use of macros and suggests the Clojure community has ability to use it where it makes sense.
Nothing can improve simplicity and readability more than macros. They make sense most of the time, almost always, not just in some edge cases.
Very simple way to put it. Probably why people argue that it's a question of nurture. When introduce to the bliss(at first) of mutable memory it a bit complicated to ask people to twist their brain, until late down the road. Now taught without this unfair comparison point, people will quickly find the "mutation free" easy to deal with with proper basic principles.
I'm not familiar with the myth of having to be a genius to learn clojure. Not having an assignment operator is the same problem anyone would have in trying to move from an imperative to a functional language.

You can be productive in clojure, I think, without being extremely fluent. But Clojure does tend to some perl-esque terseness. Browsing down this page (http://clojure.org/reader) everything is fine until you start getting into macro characters, and then the dispatch macro, and then the regex dispatch macro. I think you could write perfectly fine clojure code without knowing the details of all of these, but it can be frustrating to not know if your code is idiomatic clojure or merely badly translated from another language.

I've definitely been experiencing the frustration of not knowing whether or not I'm writing idiomatic Clojure or not. Looking through the actual Clojure + other's source code and occasionally asking on IRC when I feel like I'm doing something wrong has been a big help.
> It is a myth that you have to be a genius programmer to pick up Lisp dialects like Clojure. I think it's the opposite:

People should learn first through Racket and its awesome DrRacket. I wish I first learned programming in Racket and not Basic and Assembly.

My kids will go through Racket in a few years.

I agree. In our Programming Languages class the professor used Racket when introducing functional programming. It worked very well.
> The hard part of Clojure really boils down to one thing: you do not have an assignment operator.

Nah. You have `let` which is exactly that.