7

In his Clojure Style Guide the author writes the following,

Prefer using :require :refer :all over :use in ns macro

He does not give any explanation why this is a good idea. Is there a good reason to avoid the use of :use in the ns macro?

Kungi
  • 1,477
  • 1
  • 18
  • 34

5 Answers5

7

I'd suggest you take a look at the original discussion, which lead to this rule. The core rationale can be found here. I'll include here the start of the discussion:

There's been discussion previously about the complexity of the ns macro. In particular the fact that :use causes all vars to be referred by default is widely seen as unfortunate, and the distinction between use and require is a source of conceptual overhead for people new to the language. We can't change the fact that :use refers everything by default without breaking lots of existing code. But it would be possible to enhance :require to support referring specified vars, leaving us free to deprecate or otherwise discourage the use of :use.

Rather than this form:

(ns mork.test
  (:use [mork.stats :only [summarize-group]]
        [mork.utils :only [strip resolve-fn]]
        [clojure.test])
  (:require [mork.view :as view]
            [clojure.java.io :as io]
            [cheshire.core :as json])
  (:import (java.io PushbackReader))

We could use this:

(ns mork.test
  (:require [mork.stats :refer [summarize-group]]
            [mork.utils :refer [strip resolve-fn]]
            [clojure.test :refer :all]
            [mork.view :as view]
            [clojure.java.io :as io]
            [cheshire.core :as json])
  (:import (java.io PushbackReader))

It has been agreed upon in previous threads that keeping :import as a distinct concept from :require is desirable, and I agree that we shouldn't conflate between Clojure vars and Java classes.

There are rare cases when referring all the vars in a namespace is acceptable, (in particular when writing test namespaces it seems reasonable to bring in all of clojure.test and the namespace under test) so we should probably support :refer :all for such a case, as long as it's not the default.

Bozhidar Batsov
  • 55,802
  • 13
  • 100
  • 117
  • 1
    This should be the accepted answer. The other answers are focused on explaining very well why referring all vars is usually a bad idea, but don't discuss in any way the fact that `:refer :all`, well, refers all the vars! The reasoning is: the community has decided `:use` is a bad default and encourages bad code, therefore it's too bad it exists at all, therefore let's pretend it doesn't by always using `:require :refer :all`, even though it does the _exact same thing_. – galdre Mar 26 '19 at 15:04
  • 1
    I agree with @galdre that this is a good answer, and with galdre's implication that as far as functionality, there is no reason to prefer `:require :refer :all` over `:use`. They do the same thing, `:use` may be more concise. However, seeing this answer led me to realize for the first time that the best reason to avoid `:use` is the one expressed by @ponzao's answer: If you decide later that you don't want all of the names imported, modifying `:require :refer :all` is simpler than moving a namespace out of the `:use` sequence into the `:refer` sequence. The code is a bit cleaner, too. – Mars Jan 24 '23 at 18:52
3

My impression is that there are two reasons. 1) In doing so, you are more likely to avoid any name clashes and/or accidental overshadowing. 2) Such practices give greater transparency about the ultimate source of the functions and variables used in the code. The first one is pretty practical, and Chiron's answer provides a great demonstration.

The second one is more subtle, but if you are using a lot of libraries, it can be pretty important. Let's say you do:

(ns your-namespace
  (:use [library-one]
        [library-two]
        [library-three]
        [library-four]
        [library-five]))

And then somewhere, later in your code, you invoke:

(important-function some-arg some-other-arg)

If important-function is defined in, for example, library-three, then you (or whoever is trying to make sense of your code) have to go digging through each of those libraries' code to figure out what it does--because there's nothing to indicate which library is its source! Well, actually, if you have a REPL available, you should be able to figure it out by doing:

your-namespace> `important-function
> library-three/important-function

But that kind of hidden information is probably not the best thing to inflict on other people (including your future self) who may wish to understand your code. On the other hand, :required libraries always come with a prefix. So instead, your code would look like:

(ns your-namespace
  (:require [library-one]
            [library-two]
            [library-three]
            [library-four]
            [library-five]))

...

(library-three/important-function some arg some-other-arg)

Which makes it very clear where important-function is defined. Note that this should be equivalent to specifying :refer :all. Although :refer :all could indicate to others that you considered specifically which vars to reference, and made a conscious decision to include all of them from that library.

If you are not worried about name clashing...well you probably should be. But if you still aren't, and you want to be clear about vars' sources, then you could always do :use with :only. For example:

(ns your-namespace
  (:use [library-one :only []]
        [library-two :only []]
        [library-three :only [important-function]]
        [library-four :only []]
        [library-five :only []]))

...

(important-function some arg some-other-arg)

Obviously, if you are :useing libraries with blank :only vectors, than you might as well not :use them in the first place, I just wanted to unify with the previous examples. And doing :require may seem verbose, but you can always shorten it by aliasing with :as. Fragment example:

(ns your-namespace
  (:require [library-three :as L3]))

(L3/important-function some-arg some-other-arg)

See also, this SO question, and this guide about libraries and namespaces in Clojure.

Community
  • 1
  • 1
Omri Bernstein
  • 1,893
  • 1
  • 13
  • 17
2

Separation of concerns. require let you load namespaces, refer let you define how you refer to vars within those namespaces. use complects the two concerns.

omiel
  • 1,573
  • 13
  • 16
1

Because with :require you will have a clear indication which namespace owns a function.

(:require [clojure.json :as json])
(:require [clojure.xml  :as xml])

(json/read "file")
(xml/read  "file")

In case of :use

(read "file")

How you are going to know which namespaces owns read function? If I'm maintaining your code, I have to check ns macro and start searching for read function in each use qualified namespace.

Chiron
  • 20,081
  • 17
  • 81
  • 133
  • The question is not about :as, it's about :refer :all. When :refer is used namespace is not specified at all. – muhuk Apr 10 '14 at 09:20
  • Edit suggestion: `:json` and `:xml` should be symbols, not keywords. Also the `:require` keyword is invalid outside of `(ns ...)` which could be misleading. – Omri Bernstein Apr 10 '14 at 09:23
  • @OmriBernstein Thanks, I correct the symbols part. But for the other part, my intention is to illustrate the qualified names. – Chiron Apr 10 '14 at 09:27
1

With :require you have only one place where you are defining your namespace imports and you can easily switch from referring all functions to referring a few functions or just requiring the namespace (or mix and match however you like).

(:require [foo.bar]
          [foo.baz :refer :all :as baz]
          [foo.foobar :as fb :refer [bazbaz]])
ponzao
  • 20,684
  • 3
  • 41
  • 58