Overriding 'require' in Clojure?

clojure

Solution

You could override the `require` function and of course the overriden variant could download stuff if the namespace it was asked for is not available on the classpath. Overriding the way `:require` works in `(ns ...)` forms is, AFAIK, impossible for now due to the way in which `ns` is handled.

Note that such a 'downloading `require`' wouldn't be very helpful if you wanted to place new paths on the classpath (including new jars), as classpath injection doesn't work reliably in Clojure (due to JVM issues). There is `clojure.core/add-classpath`... but it's been marked as deprecated since forever now, its use is strongly discouraged, there are no guarantees that it will work for you and this situation isn't likely to change anytime soon. On the other hand, if you wanted to put new source files in a directory which was already present on the classpath, then that should work fine.

In case you do want to play around with overriding `require`, if you have a `foo` namespace, you could do

(ns foo
  (:refer-clojure :exclude [require])
  ; other stuff; any :requires here will work as usual!
  )

Then define your own require, using `clojure.core/require` when appropriate:

(defn require [ns-symbol]
  (do-stuff-to-obtain-the-namespace))

`clojure.contrib.find-namespaces` namespace might be helpful in finding out what's available on the classpath. (Or you could use the `the-ns` function and see if it throws an exception after an initial attempt at requiring the namespace through `clojure.core/require`.)

Note that the `binding` approach which might come to mind first (`(binding [require ...] ...)`) will not work, since `require` normally resolves to a Var interned in the `clojure.core` namespace and Vars from namespaces whose names start with `clojure` are currently directly linked by the compiler (meaning no actual Var lookup is performed at runtime, so rebinding of those Vars has no effect on the code).

The `(:refer-clojure :exclude [require])` in the `ns` form for your namespace prevents `require` from resolving to `clojure.core/require` and leaves you free to define a Var of that name in your own namespace. As mentioned above, that doesn't prevent the `clojure.core/require` Var from being accessible if you type out the fully qualified the symbol.

Problem

Would it be possible to override the 'require' command so that it will try to download a certain resource if it was not found on the local machine. For example: ``` (require 'examples.introduction) ; if not found => download from the net ; (url: http://media.pragprog.com/titles/shcloj/code/examples/introduction.clj) ```

Original source