Using Maven dependencies in a Leiningen Project

One of the much-touted features of Clojure is that it has good interoperability with Java and its vast ecosystem of libraries. Here's a quick guide on how to include a Java library (specifically, a library from the Maven ecosystem, one of the main Java build systems) in your Leiningen project. It's based on my experience integrating some functions from the Apache Commons Math library into Mondriandroid.

First, you'll have to find the coordinates for the Java library you want to use. Maven identifies a Java library by a unique identifier made up of:

For example, the library I wanted has:

There are a number of repositories where you can find the coordinates for useful libraries. For example:

These both have Leiningen dependency information available for your convenience, and it's easy enough to follow the translation. Once you've got the library's unique identifier, add it to the dependency list in your project.clj file. It should look like [<group-id>/<artifact-id> "<version>"].

Here's the math library I was using. It has a group ID of org.apache.commons, an artifact ID of commons-math3, and its version is 3.6. The entry in the :dependencies vector of my project.clj's is [org.apache.commons/commons-math3 "3.2"].

Now that you've fetched the library, you'll want use it in your Clojure code. In my case, I wanted to use the Commons' implementation of the Normal Distribution to get some normally distributed numbers. This comes down to initializing a NormalDistribution object with the desired mean and standard deviation and then calling its sample method.

To make sure it works, lets try it in the REPL:

user=> (import 'org.apache.commons.math3.distribution.NormalDistribution)
org.apache.commons.math3.distribution.NormalDistribution
user=> (def d (NormalDistribution 10 5))
#'user/d
user=> (take 3 (repeatedly #(.sample d)))
(7.592484302019164 7.84915495230975 14.292395537898699)

Success!

Because Java insists that everything live inside a class, you'll almost certainly have to create an object and call its methods--don't go looking for the pure function versions of the method you want! To make the interface between my lovely functional Clojure library and Java's objects less awkward, I hid the object-oriented code in a little wrapper:

(defn normal [mean std]
  (.sample (NormalDistribution. mean std)))

And that's it! Your project now has access to a Java (or Scala, or Groovy, etc.) library.

For more information on the details of how interoperability works, the official docs a good terse reference for the interoperation syntax. If you want a deeper (and sillier) review with some context for the Java novice, this chapter of Clojure for the Brave and True is suitable.

Nat Knight — 2016-03-06