Clojure Tidbits | Pt. 2 Require v. Import
This is another quick post on another bit of Clojure that I recently came across — the difference between require and import and why/where/how you’d use one instead of the other.
As a codebase gets larger, we naturally want to find a logical way for it to be organized, split out into separate files, etc. but still need to have a way for our code to work together. Alternatively, maybe you want to do something that requires the use of a function from the language’s standard library, but that library isn’t loaded by default. How do you get around that? Different languages have different specifics for how to link disparate bits of code together, but Clojure provides require.
require can be used in Clojure to load the code from another namespace. It’s just a function (like so many things in Clojure) that takes a few possible arguments:
There are more permutations that that, but that’s generally it. The first example will pull in all of what’s in the clojure.string namespace and combine it with your current namespace, which can result in name collisions (both your current namespace and the one you’re requiring have used the same name to refer to two different things). To help avoid this possibility, you can specify an alias for the required namespace with :as some-alias-name. In that case, anything you reference from that namespace, like the join function, must be prefixed by some-alias/ (e.g. string/join [“this” “ is a sentence” ]).
There’s a lot more to require than what I’ve mentioned, but I wanted to highlight that require is used to load Clojure code, which contrasts with import.
Import is also used load code, but instead of Clojure namespaces you’re loading Java classes. That bit seems to be relatively straightforward, but you’ll also use import to load any defrecord or deftype that you’ve created in Clojure since those actually create Java classes.
Being both new to Clojure and relatively unfamiliar with Java, all of this was a bit confusing when I was confronted with it. Compounding that confusion was the fact that require and import use slightly different naming conventions for the files they are loading: lisp-case versus snake_case for directory names, PascalCase for class names, etc.
Although both require and import can be invoked on their own, I’m finding it much more common (outside of a REPL anyway) to use them inside of the ns macro. Things generally work the same way, with a few syntactic tweaks.
Here’s an example of what require and import look like along with a namespace declaration:
To summarize, here are some guidelines
- use require for Clojure namespaces or libs
- use import for Java interop or any Clojure defrecords or deftypes