Text 8 Jan 34 notes getting started with clojure

I’m about to try to teach a bunch of people (primarily Python devs running OS X) how to use Clojure, and I’m not satisfied with any of the currently existing documentation on how to get up and running from scratch. When I was going through all this myself a few months back, there was a weird period of a good few weeks when I had basically no mental map of the Clojure ecosystem and had no idea how to assemble one.

My goal for this post is to create the resource I wish I had six months ago. I’ll assume that you’re running on OS X and have a non-zero amount of programming experience.

The Clojure Book

Your first step should be to buy and begin reading Clojure Programming. There’s another book called (confusingly enough) “Programming Clojure”, and I can’t vouch for whether it’s better or worse, but I used “Clojure Programming” and liked it very much, so it’s what I recommend. It’s written by people whose names you’re going to get used to seeing everywhere as you explore the Clojure ecosystem; all the main figures in the Clojure community seem to be inhumanly prolific.

Let’s Get Started

Now, let’s start getting your environment assembled. Get Homebrew - “the missing package manager for OS X” - if you don’t have it already, and then run

brew install leiningen

Congratulations, now you have Leiningen! (Make sure you ended up with version 2.0 or greater - you can check that by running lein --version.)

So what the hell is Leiningen?

Leiningen’s the main tool you’ll be using for:

  • starting up a REPL
  • downloading+installing libraries
  • running your programs
  • starting a server to run the webapps you’ve written

Go ahead and run `lein repl`. You’ve now got a working Clojure REPL! In addition, if you run that command from the top-level directory of one of your Clojure projects, it’ll deal with wiring up classpaths and whatnot so that you’ll be able to import and play around with your project’s code and the libraries that it depends on. We’ll get to that later. Right now, let’s create a skeleton project for us to play around with by running

lein new foo

When that’s done, cd into foo and you’ll see that it’s already got some files and directories:

[jrheard@jrheard-air:~/dev/foo] $ ll
total 16
-rw-r--r--  1 jrheard  staff   193B Jan  5 15:17 README.md
-rw-r--r--  1 jrheard  staff   263B Jan  5 15:17 project.clj
drwxr-xr-x  3 jrheard  staff   102B Jan  5 15:17 src
drwxr-xr-x  3 jrheard  staff   102B Jan  5 15:17 test

Whenever you write a Clojure library/program/anything, your source code will live in the “src” directory and your tests will live in the “test” directory. Straightforward enough. Let’s take a look around in src:

[jrheard@jrheard-air:~/dev/foo] $ cat src/foo/core.clj
(ns foo.core)

(defn foo
  "I don't do a whole lot."
  [x]
  (println x "Hello, World!"))

Looks like Leiningen’s already created a file called “src/foo/core.clj”. It’s a Clojure program that defines a namespace called “foo.core” and then declares that that namespace contains a function called “foo”. Let’s check it out. Start up a repl with `lein repl` and poke around. Remember when I mentioned earlier that leiningen takes care of setting up your classpath and associated goop such that you’re able to access your project’s code from the REPL? Check this out:

user=> (use 'foo.core)
nil
user=> foo
#<core$foo foo.core$foo@6ad591a6>
user=> (foo "jrheard")
jrheard Hello, World!
nil

Awesome - we were able to import our code and run it. The `use` function basically serves the same purpose as `from foo.core import *` would in Python, and its use in source code is similarly discouraged for the same reasons that import * is discouraged. Like import *, though, It’s pretty useful to have when you’re poking around in the REPL.

So that’s cool - we’ve created a project, it’s got code in it, we’ve found out how to start up a working REPL that can play around with that code. Bullet point 1: accomplished. Let’s take a look at the second bullet point:

Downloading and installing libraries

You’re probably used to getting your libraries by running something from the command-line,
e.g. `pip install this_great_library_i_found`, which would download the specified library and install it either globally or within your current virtualenv. Things work a little bit differently in Clojure.

First, you’ve got to find a library that looks useful. The Clojure Toolbox is a fantastic tool for this, and is the best such resource I’ve found. Let’s choose a library to play around with: making HTTP requests is fun - let’s go down to the “HTTP Clients” section and see what our options are. Looks like we’ve got to pick between clj-http and http.async.client - but how do we choose?

Currently, my favorite way of deciding between competing libraries is: pull up their respective github repos, compare the number of stars+forks, and give bonus points to any libraries that have commits from within the past month or two. Not exactly scientific, but it’s served me well so far as a good proxy for the strength of the library’s community/influence/adoption. As of this writing, clj-http has 242 stars to http.async.client’s 127, so let’s pick clj-http.

So… how do we get it?

Let’s go to clj-http’s github repo. Check out how the README’s installation section has this block of code:

[clj-http "0.6.3"]

That’s the information we need - it’s a Clojure vector with two items, the first of which is the name of the library, and the second of which identifies the most up-to-date stable version available. We’re going to add this to our project.clj, which you saw earlier when we looked at the contents of the ‘foo’ directory. Open up project.clj, it’ll look like this:

(defproject foo "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.4.0"]]) 

Note the :dependencies section - it’s a Clojure vector containing one item, and that item is itself a Clojure vector containing two items. This vector indicates to Leiningen that we want our project to run on version 1.4.0 of Clojure. Fair enough - now let’s add the clj-http vector we saw earlier. Our project.clj should now look like this:

(defproject foo "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.4.0"]
                 [clj-http "0.6.3"]])

And that’s it! We’ve now specified to Leiningen that we want the clj-http library, and which version we need. Let’s try it out - start a REPL with `lein repl`, and let’s play around with our fancy new library. Notice that Leiningen will first download clj-http before starting up the REPL - that’s because it first runs `lein deps` behind the scenes any time you ask it to do basically anything, and that causes it to scan your project.clj and make sure that it’s already fetched all the dependencies you’ve asked it to.

Okay, back to our REPL session. Looks like clj-http’s github repo’s README suggests that you require it in the REPL by running

(require '[clj-http.client :as client])

So let’s do that - it’s the same thing as `from clj.http import client` in Python (as opposed to `from clj.http.client import *`, which is again what the `use` function does.)

user=> (require '[clj-http.client :as client])
nil
user=> (client/get "http://www.yelp.com")
;; a big huge blob of data pops out!

Okay, wow, looks like that worked! That’s sort of hard to read - you’ll notice that the big huge blob of data ends with a “}”, which is a hint that it might be a Clojure map. Let’s try poking at it:

user=> (def resp (client/get "http://www.yelp.com"))
#'user/resp
user=> (type resp)
clojure.lang.PersistentArrayMap
user=> (keys resp)
(:cookies :trace-redirects :request-time :status :headers :body)
user=> (:status resp)
200
user=> (:headers resp)
{"server" "Apache", "content-encoding" "gzip", "x-proxied" "lb2",
"content-type" "text/html; charset=UTF-8", "date" "Sun, 06 Jan 2013 00:02:58 GMT",
"cache-control" "private", "vary" "Accept-Encoding,User-Agent",
"transfer-encoding" "chunked", "x-node" "wsgi, web40, www_all",
"x-mode" "ro", "connection" "close"}

And there you have it - we’ve found an HTTP client library, downloaded it, and figured out how to use it interactively in the REPL!

It took me a while to figure this all out - after beating my head against a wall for a day, I eventually had to jump into the #clojure IRC channel and plead for help. Now you don’t have to! For further reading, check out the official Leiningen tutorial.

Putting it all together

Let’s finish up by figuring out how to actually run a Clojure program. Let’s try a good old `lein run`:

[jrheard@jrheard-air:~/dev/foo] $ lein run
No :main namespace specified in project.clj.

Okay, that didn’t work. Referring back to the Leiningen tutorial mentioned earlier and doing a search for :main, we see that you can define a :main key in your project.clj definition that specifies the namespace that `lein run` will run, and that said namespace has to contain a `-main` function, which serves as the entry point into your program. Let’s add this line to our project.clj spec:

:main foo.core

And finally let’s modify src/foo/core.clj so that it looks like this:

(ns foo.core
  (:require [clj-http.client :as client]))

(defn -main
  "Prints the first 50 characters of the HTML source of yelp.com."
  [& args]
  (println (apply str
                  (take 50
                        (:body (client/get "http://www.yelp.com"))))))

Here we go - let’s try it out with `lein run`!

[jrheard@jrheard-air:~/dev/foo] $ lein run
Compiling foo.core
<!DOCTYPE HTML>

<!--[if lt IE 7 ]> <html xmlns:fb

It works!

That’s it for now - you now have a working REPL to play around with, the ability to install and use libraries, the knowledge to give your programs access to those libraries and run them, and a really good book that’ll take you through everything else you need to know about the Clojure language.

The reason I had to write this post

Clojure’s still a pretty young language. The community is extremely small relative to e.g. Python’s, and although the core language’s API is (I’m told) remarkably stable, a lot of the tools around it are new and in a state of rapid change. Add on top of that the fact that most of the up-to-date documentation you’ll find has poor SEO - to the degree that a lot of your Google searches will turn up documentation on richhickey.github.com that’s years out of date and deprecated - and you’ll find that getting started from scratch can be a little tricky.

I hope that this post has helped save you the few weeks of bewilderment that I went through when I was getting started - I promise that the joy of actually programming in Clojure is well worth putting up with these growing pains.

Assorted resources

  • Watch these two lectures by Rich Hickey, the creator of Clojure: “Are We There Yet?” and “Simple Made Easy”. In particular, watch the first one three or four times over the course of several months.
  • I’ve spent the past few weekends watching a whole lot of Clojure lectures. The official Clojure youtube channel is a great resource, and the amount of great content on InfoQ is really astounding, I’ve probably watched at least 25 lectures there.
  • The Clojure Toolbox, mentioned above.
  • Where Did Clojure.Contrib Go - you’re going to see references to libraries like “clojure.contrib.monads” as you explore. clojure.contrib no longer exists, and this page will tell you where the libraries it used to contain have gone.
  • This example project.clj shows you how to take advantage of the thousand different hooks Leiningen provides for customizing how your project is built and run.
  • Assorted core members of the Clojure community worth following on Twitter: @cgrand, @cemerick, @marick, @weavejester, @stuartsierra, @seancorfield, @Baranonsky, @richhickey
  • Read "The Joy of Clojure" once you’re done with “Clojure Programming”.
  • Heads up - Noir is deprecated. Use Compojure instead.
  1. groxx reblogged this from jrheard and added:
    many thanks! This
  2. shinook reblogged this from jrheard
  3. inetgate reblogged this from jrheard
  4. nxtw reblogged this from jrheard
  5. jrheard posted this

Design crafted by Prashanth Kamalakanthan. Powered by Tumblr.