Skip to content

Instantly share code, notes, and snippets.

@supaket
Created November 3, 2018 19:47
Show Gist options
  • Save supaket/ae97a132eb9ca0b97ac87ea828e6726a to your computer and use it in GitHub Desktop.
Save supaket/ae97a132eb9ca0b97ac87ea828e6726a to your computer and use it in GitHub Desktop.
Setting Up Clojure on OS X

Setting Up Clojure on OS X

I spent a lot of time trying to find a pretty optimal (for me) setup for Clojure… at the same time I was trying to dive in and learn it. This is never optimal; you shouldn't be fighting the environment while trying to learn something.

I feel like I went through a lot of pain searching Google, StackOverflow, blogs, and other sites for random tidbits of information and instructions.

This is a comprehensive "what I learned and what I ended up doing" that will hopefully be of use to others and act as a journal for myself if I ever have to do it again. I want to be very step-by-step and explain what's happening (and why) at each step.

I appreciate the effort you've put into documenting this, but there are a number of inaccuracies here that need to be addressed. We get a lot of people in the #clojure channel who find one-off blog posts that are either out of date or misleading and end up frustrated and confused when the projects' official documentation covers what they really need to know. If you find the documentation lacking, please consider contributing improvements to it! Maintainers are usually happy to find people willing to help with documentation.

I've added my comments below in block-quotes.

Step 1: Getting Clojure (1.3)

I was already familiar with (and had) Homebrew (http://mxcl.github.com/homebrew/). If you don't already have Homebrew installed, I highly recommend getting it. I won't cover that hear as it's not that difficult, but I also don't want to be responsible for you accidentally blowing away your /usr/local path… ;-)

But, assuming you have Homebrew installed, let's install the latest version of Clojure:

$ brew install clojure

This is actually unnecessary; Clojure is a library rather than an application designed for end-user interaction. It doesn't even come with an official launcher; each individual packager rewrites their own from scratch (introducing new quirks and bugs) because they don't understand that Clojure isn't designed for this. Avoid.

Step 2: (Mis-) Understanding Clojure.Contrib

When starting to learn Clojure, many people are like myself and want to dive into a personal project immediately. For myself, I have several common apps I like to make with any new language to get a sense of how it works. I chose a simple telnet-based chat server.

I performed a quick Google search for "clojure sockets" and "clojure streams" and was immediately presented with several pages where various libraries (in clojure.contrib) could help me out. I then spent the next several hours trying to figure out how to get them to work.

The problem is that - with Clojure 1.3 - the "contrib" library was removed in favor of breaking the libraries up and either putting them into their own library or embedding them into Clojure's core. This is a good page to use as a reference for what's happened to various contrib libraries you might come across online:

http://dev.clojure.org/display/design/Where+Did+Clojure.Contrib+Go

Just know that if you are reading a tutorial on Clojure that makes use of a clojure-contrib library, it's out of date and you might need to look at the above link to see what happened to the library in question.

Step 3: Leiningen and Clojure Projects

The next step was learning how to setup a Clojure project. Almost all Clojure tutorials and examples I came across would mention a project.clj file, but then fail to give any details. I wasted a ton of time learning about the JVM's CLASSPATH (note: I'm new to the JVM) and trying to "make it work" unsuccessfully. As it turned out, there's a wonderful tool out there created by Phil Hagelberg that helps with this called Leiningen (https://github.com/technomancy/leiningen).

Installing Leiningen

Homebrew again comes to the rescue and can install it for you:

$ brew install leiningen

See the Packaging page of Leiningen's wiki for other options: https://github.com/technomancy/leiningen/wiki/Packaging On the other hand, a manual install of Leiningen is very easy too.

Once installed, Leiningen solves many important problems for you, mostly dealing with Java's CLASSPATH, project setup, dependencies, and building.

Your First Project

Once Leiningen is installed, you can use it to create a new Clojure project that you can begin to toy with.

$ lein new myproj

This will create a new folder myproj/ off the current directory, along with a simple skelton (including a .gitignore file, how helpful):

+
| project.clj
| README
+ src/
| + myproj/
| | | core.clj
+ test/
| + myproj/
| | + test
| | | core.clj

Now, if take a quick look at project.clj we can see a basic setup for our first project:

(defproject myproj "1.0.0-SNAPSHOT"
  :description "FIXME: write description"
  :dependencies [[org.clojure/clojure "1.3.0"]])

For right now, the most important part of this file is the :dependencies section. This is a list of what myproj requires in order to load, compile, and run.

If you remember the hint from earlier, the JVM has a CLASSPATH environment variable that is used find required libraries. What Leiningen does for you is take your list of dependencies and install them to a known location for your project. This is done with the deps command:

$ lein deps
Copying 1 file to ./myproj/lib

Running the deps task by hand is almost never necessary. Leiningen knows when it needs to be run.

If you take a look there is now a lib/ folder in your project directory and it contains the single jar dependency for Clojure that was specified in your project.clj file.

At this point, we have everything we need to start playing around and making interesting things happen. We can use Leiningen to start Clojure now from within our project folder and have access to all of our dependencies and project source files.

$ lein repl
REPL started; server listening on localhost port 17970
user=> 

For now we're going to move onto the next step, but we'll be coming back to Leiningen later…

Step 4: A Development Environment

This is where - as with any programming language - the community diverges incredible into a whole plethora of recommendations. And, as with any language that's remotely based on Lisp, Emacs + Slime will be the #1 recommendation.

I've used Emacs for years for Common Lisp development with Slime and I love it. And I've also used Vim for lots of editing and it has lots of wonderful features as well. I say that because my recommendation will not use either, and I wanted you to know that I do use them and love them. So my recommendation to use something else is based on the fact that I put serious effort into getting either to work, failed miserably, and don't believe anyone should have to go through that kind of hell (that - and frankly - Emacs/Vim isn't for everyone).

The most important point here is to use what you know. Don't try to learn a new editor at the same time as a new language. Stick with what you know, and if you must, consider picking up an improved editor once you know your way around Clojure.

Dismissing Emacs + Slime and Vim + VimClosure

In case you are interested in plowing your own path at this point and making Emacs work for you with Slime, or VimClosure with Vim, here's a couple links/hints to help you get going:

Emacs + Slime

First, you can download a Clojure mode for emacs here:

https://github.com/jochu/clojure-mode

Far and away the most common reason people have trouble getting started with Emacs and Clojure is that the Web is littered with well-meaning posts like this that contain misleading information.

The link above is to an old, unmaintained version of clojure-mode; in fact once you click on the readme it even states that the repository is deprecated if favour of https://github.com/technomancy/clojure-mode.

Now, the Swank server for Clojure is old. This means you're going to have to get an old version of Slime to work with.

This can be done many ways, and if you know Emacs then this shouldn't be a serious problem for you. Simply put, put a current version of Slime in .emacs.d/slime-current and the old version for Clojure in .emacs.d/slime-clojure, then make two functions, based on which one you want to use:

;; load slime for common lisp
(defun slime-common-lisp ()
  (interactive)
  (add-to-list 'load-path "~/.emacs.d/slime-current")
  (require 'slime)
  (slime-setup '(slime-fancy))
  (slime))

;; load slime for clojure
(defun slime-clojure ()
  (interactive)
  (add-to-list 'load-path "~/.emacs.d/slime-clojure")
  (require 'slime)
  (slime-setup '())
  (slime-connect "localhost" 4005))

Note: I cannot claim credit for this… This was put together after a very late night of scouring the internet and coming across several posts on StackOverflow which lead me to this solution. I wish I had links to post, but I've long-since closed the pages. If you have them, send them my way and I'll update this post.

This is much more complicated than you need when getting started. Once you install clojure-mode and Leiningen, you don't need to install slime. Simply run M-x clojure-jack-in and it will install the correct version of slime for you. The only time you would need to manually install slime is if you need to simultaneously work with Common Lisp, or if you want to connect to a swank server not started by Leiningen. If you are just getting started, avoid this and stick with the official documentation: https://github.com/technomancy/swank-clojure

Next, you'll need to update your project.clj file and add a new dependency (as a plugin) for Swank. This is easy enough to do, simply add the following line to the file:

:plugins [[lein-swank "1.4.4"]]

The lein-swank plugin should not be put in project.clj; it should be installed on a user-wide basis. The way to do this is different in Leiningen 1.x vs Leiningen 2.x, so see the official documentation for details.

Once this has been done, you can then use Leiningen (yep!) to start up a Swank server for you:

$ lein swank
Connection opened on localhost port 4005.

You should now be able to launch Emacs, M-x clojure-mode, and connect to the Swank server with M-x slime-clojure.

Vim + VimClojure

I'm not going to spend much time here. Sorry. I know Vim well enough to use it and to make a few Vim scripts, but not well enough to follow a lot of the pages I found discussing screens, pathogen, etc.

Needless to say I tried, but at 2am in the morning, my brain wasn't functioning well enough to comprehend everything I was reading. Instead, here are several links which might help you if this is a path you want to go down…

Note: References made to installing nailgun you can ignore and just use Homebrew: brew install nailgun.

I don't keep track of this as closely, but it may be worth noting that the author of VimClojure is working on a new nREPL-backed Clojure adapter for Vim written in Haskell. nREPL comes with Leiningen 2.

Step 5: Using Local Libraries

Leiningen uses a Java program called Maven to get at libraries. I'm not even close to a beginner at using/understanding Maven, but you should generally be aware of it and where your jars are being copied from (via lein deps).

Understanding Maven Repositories (basic)

In your home directory is a .m2/ folder. Inside that is a repository folder. At this point, if you ls you'll see a lot of directories.

One in particular that should stand out is the org folder, inside which is the clojure folder, inside that is another clojure folder and, finally, inside that, is a list of one or more version folders. And, if you look inside the 1.3.0 directory, you'll see the jar for clojure-1.3.0.

Now, if you reference back to your project.clj file, in the dependencies section, do you remember this?

:dependencies [[org.clojure/clojure "1.3.0"]]

So, now you can see how that dependency description turned into a jar file that was copied into the lib/ folder of your project:

[org.clojure/clojure "1.3.0"] -> 
	~/.m2/repository/org/clojure/clojure/1.3.0/clojure-1.3.0.jar

So far so good.

Installing Libraries

Now, let's say you find a nice Clojure library somewhere in GitHub that you would like to use. How do you install it and use it in your own projects? Again, Leiningen is here to help us out. There is a wonderful plugin for Leiningen called "lein-localrepo":

https://github.com/kumarshantanu/lein-localrepo

Check out the README.md file to see how to install it for different versions of Leiningen. I'm going to assume that you are using a version < 2.0 for the rest of this tutorial (use lein version to see which version you have installed).

$ lein plugin install lein-localrepo "0.3"

Once installed, you can then use Leiningen and the localrepo plugin to add Clojure libraries others have written to your Maven repositories and reference them in your project.clj file!

The lein-localrepo plugin is actually only useful for one very rare scenario: needing to work with bare jar files downloaded off the web that are not found in any Maven repositories. Any library author who knows what they're doing will have their jars published in a repository; if you find a library that doesn't then the first thing to do is to report a bug with the maintainer. If they refuse to publish their library, then you should get it into a private repository, perhaps something hosted on S3: https://github.com/technomancy/s3-wagon-private. Needing to deploy a bare jar file to your local repository is almost never necessary, and a manual install step like this is harmful to repeatability: https://github.com/technomancy/leiningen/wiki/Repeatability

An Example

Phil Hagelberg (creator of Leiningen) has a nice port of clojure.contrib's server-socket library for Clojure 1.3 that we'll download, compile, install, and load into our project.

Downloading

First, let's download and it:

$ git clone git://github.com/technomancy/server-socket

Cloning the project from source is not necessary unless you want to make changes to it. If you want to use it as-is, you should just add it to :dependencies in project.clj as per the last step below, and it will get pulled from Clojars automatically: http://clojars.org/server-socket.

Compile a JAR

Next, we need to compile it. This uses Leiningen since server-socket is a Leiningen-based project. If it wasn't, you would use a different method to build a jar.

$ lein jar
Created ./server-socket-1.0.0.jar

This actually does not compile the jar; it only packages it.

Installing the JAR

Now, let's decide where we're going to install it to. We can install it where-ever we want, but sometimes Leiningen has a suggestion. Let's ask where Leiningen and the localrepo plugin thinks it should go:

$ lein localrepo coords server-socket-1.0.0.jar
server-socket-1.0.0.jar server-socket/server-socket 1.0.0

The output says that - in the ~/.m2/repository folder - it thinks it should go under server-socket/server-socket with version "1.0.0". So, let's follow the suggestion:

$ lein localrepo install server-socket-1.0.0.jar server-socket/server-socket 1.0.0

Actually, all that's necessary here is a simple "lein install". Because the project comes with a project.clj file, Leiningen can put it in the right place itself.

Let's verify that it went where we expect it to go:

$ ls ~/.m2/repository/server-socket/server-socket/1.0.0
server-socket-1.0.0.jar

Yep! It's been installed successfully and is ready for use.

It's important to note that we didn't have to install it to the recommended location. We could have installed it somewhere else. For example, we could have chosen local.repo/server-socket instead. It's entirely up to us.

Updating the Project File

In order to have Leiningen install the library for our project to use, it will need to know about it. So, at this point we'll need to update the :dependencies section of our project.clj file:

:dependencies [[org.clojure/clojure "1.3.0"]
               [server-socket/server-socket "1.0.0"]]

Because this library is already on Clojars. you could have started out with this step and it would have worked just fine.

And, once saved, we can go to the project directory and update our installed dependencies for the project:

$ lein deps
Copying 2 files to /Users/jeff/Projects/myproj/lib

Loading It Up!

Finally, let's head back into Sublime, to our project.clj file, and relaunch the Sublime REPL for Clojure. Once it's up and running, let's see if we can use it…

user=> (use 'server.socket)
nil

Yep!

Note: We knew it was server.socket and not server-socket because in the GitHub repository for server-socket the class was located at src/server/socket.clj and socket.clj had the namespace server.socket.

Conclusion

Being new to Clojure, the above took me quite a while to figure out (a couple late nights), and I'm sure there's still quite a lot I'm missing or don't fully grok (especially with respect to Maven). All the tutorials and guides I came across all seemed to assume an understanding of Java and/or other aspects of what was happening. Here's hoping that this guide can help others so that they don't have to spend forever just trying to get a simple Clojure project up and running and give this language (and Lisp) the chance it deserves.

One good resource here is the Leiningen tutorial. "lein help tutorial" covers a lot of this stuff. If there are things that are still fuzzy from the tutorial I would be glad to get feedback on it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment