This Clojure Thing

I like languages. I don't have much of an ear for natural languages, but after some decades of playing around with computers I do know a little bit about programming languages. Once in a while I set out to learn a new one.

So why learn Clojure? First, the learning part: I still remember how much of an insight learning Erlang was a few years back. Not only because I was able to (poorly) program some Erlang, but also because it got me to think about more general topics (fault tolerance and concurrency, for instance) in a new way. And, besides Erlang being a pretty cool language, it also was a lot of fun. It also was bit of a gateway drug to functional programming (though I'm still not any good at it).

Also there seemed to be a lot of features that make sense to me: first and foremost great concurrency story, but also the REPL, Java integration (very important for obvious practical reasons), immutable data structures and software transactional memory. Clojure has had a bit of hype a few years back and may not always have lived up to the expectations (for one, it's performance still isn't that great it seems), but I don't care too much about that right now.

What I want to do here is to learn some basic Clojure and write about it in the process. I don't expect to become a Clojure crack in a few days -- just to have bit of fun learning something new. I plan to do it the slow way, start with the basics (dry stuff -- scalars, funcs, lists, ...) and go up to the more involved things.

A pic from Upper Austria, last summer -- no reason

Writing about it gives me some more clarity, and ultimately helps me grok the subject matter. I'm going to keep this very hands-on, partly because I've already done some reading, and also because I learn best that way.

The resources I'll follow are "The Joy Of Clojure", 2nd ed. and the tons of online texts available.

Getting Started

I've installed Clojure via Leiningen. Download script, run, done. "Leiningen is for automating Clojure projects without setting your hair on fire." it says, and so far my hair is fine.

There's some excellent Emacs support for Clojure. I've installed the clojure-mode, clojure-test-mode and cider packages via Marmalade. After installation, I had to tell cider the path to lein in my $HOME/bin. Having done that, running M-x cider-jack-in drops me into a Clojure shell in Emacs -- very painless; I remember it took me quite a bit of fiddling to get this kind of setup with Erlang. I've also set up paredit -- an Emacs mode to automatically handle parentheses and suchlike.

Cracking open the book

I'm skim-reading through TJoCs' introductory sections. Having already had some exposure to functional programming they're preaching to the converted a bit when talking about the benefits of functional programming. However I found the material on Lisps' regular structure and how this neatly ties into the "code is data" philosophy pretty good, so it was still very much worthwhile.

REPL doodling

Interactive shells are a good way of exploring a language, and I plan to do lots of micro-examples in the shell. I also did this when learning Erlang. I think that helps on several levels: a) it gives me a chance to test my grasp of a language concept, b) it gives my fingers a chance to absorb the language into muscle memory, and c) by doing small variations on known-good examples I can test the boundaries of a construct.

Especially b) is quite important. I guess I fell into most of the traps Lisp has set for someone who mostly grew up on C, Python et al., simply because I more or less automatically type some constructs. E.g. I've run stuff like a + 2 and stared for a moment at the error message before realizing that I've just typed infix, not prefix notation -- again.

So, I want to do a lot of little one- or two-liners with micro-examples, no matter how contrived or silly they may seem, but hopefully are illustrative of some language aspect.

Scalars

Numbers

First up, integers. By default they are Long but are automatically expanded to BigInts. Radixes other than decimal can also be used as well as the old 0xDEADBEEF and 0777 notations:

user> 1
1
user> -1
-1
user> 99999999999999999999999999999999999999
99999999999999999999999999999999999999N
user> 0xff
255
user> 2r1100
12
user> 010
8
user> 32r11
33

Floating point can be arbitrarily precise. They can be given in dot-notation or scientific:

user> 1.1
1.1
user> 1.1e10
1.1E10

Clojure has built-in support for rationals:

user> 1/9
1/9
user> 2/4
1/2
user> 4/4
1

Symbols

AKA names for function params, local vars, globals, etc. Syntax is more permissive than in other langs:

user> (def x 23)
#'user/x
user> x
23
user> (def i-am-a-pretty-long-name-for-something-simple! x)
#'user/i-am-a-pretty-long-name-for-something-simple!
user> i-am-a-pretty-long-name-for-something-simple!
23
user> (def "illegal" 3)
CompilerException ...

Keywords

Keywords are symbols that always evaluate to themselves. AFAICT the same thing as Erlangs' atoms: constants that can be used to (very efficiently) tag other values or serve as components in enumerations. I guess in C you'd probably use power-of-two preprocessor #defines to achieve something not too dissimilar, eg. #define FOO 2; #define BAR 4; #define QUUX 8; ...

Strings and characters

Pretty standard unicode strings; multiline strings allowed:

user> "α and ω"
"α and ω"
user> "embedded
 newlines are ok"
"embedded\n newlines are ok"

Character literals:

user> \A
\A
user> \α
\α
user> \
\space

Collections

Lists and function calls

Lisp == LISt Processing, so no wonder lists get some special treatment. Most importantly, Clojure will always try to resolve the first element of the list to a function (or macro or operator) and treat the rest of the list either as function args (after evaluating them) or pass them for handling to the operator or macro. Lists are written as elements in parentheses, separated by spaces. Eg. the plus operator, and several numbers in a list:

user> (+ 2 3)
5
user> (+ 1/4 2/4 1.0)
1.75

The function vector takes its arguments and puts them in a vector:

user> (vector 1 2 3)
[1 2 3]

Vectors

Vectors are lists without that Lispy evaluation, ie. all elements eval to themselves (like you would expect). Like lists, vectors may take any kind of element.

Notation is in square brackets:

user> [\a [2] [] () (+ 2 1)]
[\a [2] [] () 3]

Maps

Yay, maps! We Pythonistas love maps! Maps are dictionaries are hashmaps are associative arrays; but actually I like the term map best. Notation: curly brackets with key-value pairs.

user> {1 "one", 2 "two", (+ 1 2) "three"}
{1 "one", 2 "two", 3 "three"}
user> {1}
RuntimeException Map literal must contain...

The commas are actually optional. Unlike Python, maps as keys seem ok:

user> {{} "empty map" nil "nil" () "empty list"}
{{} "empty map", nil "nil", () "empty list"}

Sets

Unique items:

user> #{\a "a" :a}
#{\a "a" :a}
user> #{1 1}
IllegalArgumentException Duplicate key...

Vars

Symbols can name vars. Vars may or may not be bound to a value. They may be shadowed in a local scope.

user> (def x)
#'user/x
user> x
#<Unbound Unbound: #'user/x>
user> (def x 23)
#'user/x
user> x
23

That's it for now

Pretty dry stuff for now. Well, next thing up will be functions, this should be more interesting.

 · 
peter
 ·