Why should you prefer :require :refer :all over :use in clojure - clojure

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?

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.

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.

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.

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]])

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.

Related

Group multiple namespace definitions under one namespace

Suppose I have the namespaces foo.car.components.engine, foo.car.components.transmission, foo.car.components.brakes.
In foo.car.components.engine there is (defn engine [] ...), foo.car.components.transmission there is (defn transmission [] ...),
foo.car.components.brakes there is (defn brakes [] ...).
I'd like to make these available in foo.car.components such that other namespaces only need to require foo.car.components to use engine, transmission, and brakes.
The following works, but I'm wondering if there are cleaner ways to do this or if it's even good practice.
(ns foo.car.components
(:require
[foo.car.components.engine :as engine]
[foo.car.components.transmission :as transmission]
[foo.car.components.brakes :as brakes]))
(def engine engine/engine)
(def transmission transmission/transmission)
(def brakes brakes/brakes)
I don't offhand know of a better way, but this way does come with downsides, so take these into consideration when deciding if you want to use this:
It doesn't transfer Meta information to the "wrapper", so any docstrings/other information attached to the main function won't show up in IDEs when you use the wrapper.
Along the same vein, because the wrapper doesn't have an argument list, if you ctrl+q the wrapper functions, it also won't show the available argument lists of the main function.
Having said that, Seesaw, a major Clojure library that wraps Swing does use this "technique". If I ever forget the docs/arguments of a function that has a "convenience wrapper", I just have to hit ctrl+b twice (in IntelliJ), and it will take me to the original source where I can look it over. It's ironically inconvenient, but I guess that's the price for convenience elsewhere.
To get around these faults, you could write a function (or a macro that wraps def) that transfers Meta information. Considering argument list information is stored as Meta information, that might be enough to overcome the faults.
This answer doesn't really answer your question, so I hope someone else is able to give some insight here. I thought that this was relevant information though.
You can use:
import-vars
from https://github.com/ztellman/potemkin
It doesn't seem that this feature is provided by Clojure API. You can consider https://github.com/ptaoussanis/encore look for defalias.
On the other hand, if you have brakes, transmission and engine as public interfaces and you can use them separately why would you merge them? As opposite, you can provide all definitions in components or even car which will use in turn brakes, transmission and engine. In such way, it is not necessary to expose all components.
I think the way you're doing it is the best way, because it makes explicit the sources of the defs in each namespace. If you have large namespaces with a lot of functions, you could write a basic helper function to do this for you:
(ns foo.utils)
(defn export-refs
[target-ns source-namespaces]
(doseq [ns source-namespaces
[sym f] (ns-interns ns)
:let [existing (get (ns-interns target-ns) sym)]]
(when (and existing (not= (var-get existing) f))
(throw (Exception.
(format (str "Cannot refer to symbol %s in %s from %s, because that symbol "
"already exists in the target namespace")
sym (ns-name ns) (ns-name target-ns)))))
(intern target-ns sym f)))
(ns foo.car.components.engine)
(defn engine [] (println "engine"))
(ns foo.car.components.transmission)
(defn transmission [] (println "transmission"))
(ns foo.car.components.brakes)
(defn brakes [] (println "brakes"))
(ns foo.car.components
(:require [foo.utils :refer [export-refs]]))
(export-refs 'foo.car.components '[foo.car.components.engine
foo.car.components.transmission
foo.car.components.brakes])
(ns user
(:require [foo.car.components :refer [engine transmission brakes]]))
(engine) ;; Prints "engine"
(transmission) ;; Prints "transmission"
(brakes) ;; Prints "brakes"
Whether it's "good practice" or not is up to you. Obviously it has the advantage of splitting code into smaller files with specific functionality, while allowing the import of only a single namespace. The disadvantage is that there's a bit of indirection in where the functions come from, which will make finding the function sources more difficult, and there's more risk of name collisions.

Are :refer and :as to be used simultaneously?

There's something I don't understand very well when I read Clojure code (say from various projects on GitHub): in the :require expression, are you supposed to use :as when you're only using functions that you're referring to using :refer? Also I'm not too sure if there's any difference between Clojure and ClojureScript from that standpoint, nor if there's any convention.
For example (I'm using the cats library for the examples but that's just an example) if I do this:
(ns example.core
(:require
[cats.core]
[cats.monad.maybe :as maybe]))
from, say, the REPL I can do this:
REPL> (cats.core/mappend (maybe/just 1))
But it's not very convenient, so I may do this instead:
(ns example.core
(:require
[cats.core :as m]
[cats.monad.maybe :as maybe]))
REPL> (m/mappend (maybe/just 1))
Now in case I use mappend (and a few others) all the time, I could do:
(ns example.core
(:require
[cats.core :refer [mappend]]
[cats.monad.maybe :as maybe]))
REPL> (mappend (maybe/just 1))
So my question is: is there any use in using both :as and :refer, like in the following example?
(ns example.core
(:require
[cats.core :as m :refer [mappend]] ; does this make any sense?
[cats.monad.maybe :as maybe]))
I understand that I may be using, say, mappend all the time and may want to refer to it directly as mappend but bind not that often as to using :refer but yet often enough so that writing cats.core/bind is not convenient, so I'd use m/bind?
So is there any rule? Like if you're using :as you should not use :refer, or the other way round? Or that you should always use both?
What about ClojureScript, is it working in exactly the same way?
You're right on the use case; things that you use quite often, you may want to :refer to directly, otherwise, just use the :as alias to access it. There's no problem with doing both.
I personally tend to prefer :as unless it's very clear what namespace the :refered function came from (e.g. I would recognize >!! without a prefix, but find m/bind more readable).
The most widely used style guide afaik suggests that using them in conjunction is fine, but prefer :as to :refer in most cases: https://github.com/bbatsov/clojure-style-guide#comprehensive-ns-declaration
Clojurescript should be exactly the same for :refer and :as in ns.

Access requirements from outside of the namespace

Imagine there is the following require-statement inside a namespace:
(ns my.core
(:require '[mylib.sth :as thing]))
(def somevar 123)
Is there a way to access mylib.sth via thing also from outside this namespace? I mean to somehow get the same behavior as for the definition somevar:
(ns somethingelse)
my.core/somevar
;; =123
(my.core/thing/myf "param") ;; something like this
;; ...
resolve and ns-resolve were made for this situation.
They will return nil if the symbol is not found, otherwise they return the var, which you can deref in order to get the current bound value.
user=> (ns my.test)
nil
my.test=> (def hidden 5)
#'my.test/hidden
my.core=> (ns my.core (:require [my.test :as t]))
nil
my.core=> (in-ns 'user)
#object[clojure.lang.Namespace 0x25930632 "user"]
user=> #(resolve 'my.test/hidden)
5
user=> #(ns-resolve 'my.core 't/hidden)
5
This works, but it's also a last resort. It should be reserved for situations where you are writing code that uses namespaces and bindings that you expect to find at run time that cannot be accessible at compile time. For example I use resolve to avoid transitive AOT of my project while compiling a stub that is callable from Java; the stub -main invokes require and then resolve at runtime, using the resolved values to access the real code.
If all you are looking for is a convenience or syntactic shortcut, the better option is to explicitly require a namespace if you want to use its values.

When declaring a usage of namespace is required and when it's not?

I have two "lein" projects with exactly the same piece of code in two test files:
... clojure.set/union ...
The first project compiles and runs successfully. The second one errors out with:
Exception in thread "main" java.lang.ClassNotFoundException: clojure.set, compiling:(foo/bar.clj:14)
...
Caused by: java.lang.ClassNotFoundException: clojure.set
...
I can only make the second project run if I add explicit :use in bar.clj
(:use clojure.set)
The first project doesn't have this statement though. Why different behaviours?
UPDATE:
The first project, where the exception is not happening, has the following declaration in project.clj:
:eval-in-leiningen true
You only have to declare the use of a namespace if you want a local symbol to refer to a name in that namespace. Otherwise you can spell out the full name of each var you want to use. See the first page of http://clojure.org/namespaces
Normal functions are usually stored in vars which you can access either by spelling out the name completely (clojure.core/+ 1 2) or by causing a symbol in the enclosing namespace to refer to it. As an example we can start with a completely blank namespace called bar. It won't even start with the core Clojure functions in it:
foo> (in-ns 'bar)
#<Namespace bar>
bar> (+ 1 2)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: + in this context, compiling:(NO_SOURCE_PATH:1)
Though we can use the + function if we spell out the full name of the var that contains it.
bar> (clojure.core/+ 1 2)
3
then you can make the symbol + refer to the same var it does in clojure.core by calling refer
bar> (clojure.core/refer 'clojure.core)
nil
bar> (+ 1 2)
3
As for your errors, situations like this tend to occur when:
you ran (use 'clojure.set) or (require 'clojure.set) from the REPL while in that namespace
the code that calls a function from clojure.set is not actually being called causing you not to see the errors.
it's declared in the (ns foo.bar (:use clojure.clj)) form at the top of the file in one project and not in the other
Dependencies in Clojure behave much like in most other dynamic languages: until you explicitly require a namespace, no symbols from it will be available because the namespace will not have been loaded into the runtime; however, once a namespace is loaded, its symbols are available from all namespaces, which explains the behavior you witness. The inconsistency results from the details of the order in which your files are loaded into the runtime.
Other aspects of the namespace dependency issue deal only with the level of convenience with which you dereference the foreign-namespace symbols:
you can declare a namespace prefix to use instead of the full name:
(require [clojure.string :as s])
This gives you (s/join "," coll)
you can refer certain symbols from the foreign namespace into the home namespace. This binds the local symbols to their same-named counterparts in the foreign namespace:
(require [clojure.string :refer [join]])
which gives you (join "," coll)
you can both declare a prefix and refer specific symbols:
(require [clojure.string :as s :refer [join]])
which gives you (join "," coll) as well as (s/join "," coll)
you can refer the whole namespace into your home namespace:
(require [clojure.string :refer :all])
NOTE: the demonstrated syntax works only inside the (ns ...) form. When require is used as standalone form, all symbols must be explicitly quoted.
As you can see, all you need (as of Clojure 1.4) is require and use is now just a minor convenience for the cases where you don't need to declare a prefix.
(use clojure.string) is the same as (require [clojure.string :refer :all])
(use [clojure.string :only [join]]) is the same as (require [clojure.string :refer [join]]).

Clojure : loading dependencies at the REPL

I recently learned (thanks to technomancy) that, at the REPL ---
This fails:
user=> (:require [clojure.set :as set])
java.lang.ClassNotFoundException: clojure.set (NO_SOURCE_FILE:24)
Whereas this succeeds :
user=> (require '[clojure.set :as cs])
nil
at loading the clojure.set class.
Context: The former line was copied from a namespaced source file.
My primary question is : What is the change we have made, by swapping the : and ' characters, which now allows for success of the latter command ?
My 2nd question is , in general - what are the guidelines for doing things at the REPL --- as compared with doing things in normal clojure source files ? Assume here that we can load our repl from the root of a LEININGEN project, so at least the jars will be available on disk in the dependencies sub directory.
I'll go from high-level down to your particular problem:
How Clojure (or LISPs) Generally Work
REPLs, or Read-Eval-Print Loops are the core of how LISPs are designed:
The reader converts a stream of characters into data structures (called Reader Forms).
The evaluator takes collection of reader forms and evaluates them.
The printer emits the results of the evaluator.
So when you enter text into a REPL, it goes through each of these steps to process your input and return the output to your terminal.
Reader Forms
First some, clojure reader forms. This will be extremely brief, I encourage you to read or watch (part 1, part 2) about it.
A symbol in clojure is form that can represent a particular value (like a variable). Symbols themselves can be pass around as data. They are similar to pointers in c, just without the memory management stuff.
A symbol with a colon in front of it is a keyword. Keywords are like symbols with the exception that a keyword's value are always themselves - similar to strings or numbers. They're identical to Ruby's symbols (which are also prefixed with colons).
A quote in front of a form tells the evaluator to leave the data structure as-is:
user=> (list 1 2)
(1 2)
user=> '(1 2)
(1 2)
user=> (= (list 1 2) '(1 2))
true
Although quoting can apply to more than just lists, it's primarily used for lists because clojure's evaluator will normally execute lists as a function-like invocation. Using the ' is shorthand to the quote macro:
user=> (quote (1 2)) ; same as '(1 2)
(1 2)
Quoting basically specifies data structure to return and not actual code to execute. So you can quote symbols which refers to the symbol.
user=> 'foo ; not defined earlier
foo
And quoting is recursive. So all the data inside are quoted too:
user=> '(foo bar)
(foo bar)
To get the behavior of (foo bar) without quoting, you can eval it:
user=> (eval '(foo bar)) ; Remember, foo and bar weren't defined yet.
CompilerException java.lang.RuntimeException: Unable to resolve symbol: foo in this context, compiling:(NO_SOURCE_PATH:1)
user=> (def foo identity)
#'user/foo
user=> (def bar 1)
#'user/bar
user=> (eval '(foo bar))
1
There's a lot more to quoting, but that's out of this scope.
Requiring
As for require statements, I'm assuming you found the former in the form of:
(ns my.namespace
(:require [clojure.set :as set]))
ns is a macro that will transform the :require expression into the latter form you described:
(require '[clojure.set :as set])
Along with some namespacing work. The basics are described when asking for the docs of ns in the REPL.
user=> (doc ns)
-------------------------
clojure.core/ns
([name docstring? attr-map? references*])
Macro
Sets *ns* to the namespace named by name (unevaluated), creating it
if needed. references can be zero or more of: (:refer-clojure ...)
(:require ...) (:use ...) (:import ...) (:load ...) (:gen-class)
with the syntax of refer-clojure/require/use/import/load/gen-class
respectively, except the arguments are unevaluated and need not be
quoted. (:gen-class ...), when supplied, defaults to :name
corresponding to the ns name, :main true, :impl-ns same as ns, and
:init-impl-ns true. All options of gen-class are
supported. The :gen-class directive is ignored when not
compiling. If :gen-class is not supplied, when compiled only an
nsname__init.class will be generated. If :refer-clojure is not used, a
default (refer 'clojure) is used. Use of ns is preferred to
individual calls to in-ns/require/use/import:
REPL usage
In general, don't use ns in the REPL, and just use the require and use functions. But in files, use the ns macro to do those stuff.
The difference is that require is a function used for importing code, whereas :require is a keyword.
Remember what happens when you use a keyword as a function:
=> (type :require)
clojure.lang.Keyword
=> (:require {:abc 1 :require 14})
14
it looks itself up in the map. So when you pass [clojure.set :as set] to a keyword, it's trying to evaluate that to a vector, and fails because it doesn't know what clojure.set is. The Clojure docs say:
Keywords implement IFn for invoke() of one argument (a map) with an
optional second argument (a default value). For example (:mykey
my-hash-map :none) means the same as (get my-hash-map :mykey :none).
You may have been confused by the ns macro:
(ns foo.bar
(:refer-clojure :exclude [ancestors printf])
(:require (clojure.contrib sql sql.tests)) ;; here's :require!
(:use (my.lib this that))
(:import (java.util Date Timer Random)
(java.sql Connection Statement)))
ns macro:
When you type:
(ns some-great-ns
:require my-form)
you use the :require reference in which you state what would you like to use from the given namespace. It is equivalent to writing:
(in-ns 'some-great-ns)
(require 'my-form)
Notice that in the ns form (unlike the in-ns function call), you don’t have to quote your symbol with '. You never have to quote symbols within ns.
require function
As stated, can run: (require 'some-great-ns) in some given namespace so you could use it. To use it, you'll have to use full qualified name, unless you also use: refer function: (refer 'some-great-ns) right after you required the namespace.
You can do those both functions in one: (use 'some-great-ns). Now you don't need to write: (some-great-ns/my-form). Simply: my-form.
And of course you can also use the :as, :exclude, :only and :rename keywords in both the macro reference and in the function.
Differences between the macro and the function:
As stated above, usage of symbols in function, no need in the macro
You can require multiple libraries in a (:require) reference as follows:
(ns my-great-namespace.core
(:require [some-other-ns.a.b :as ab]
[some-other-other-ns.c.d :as cd]))
Where in function writing you should write 2 lines:
(in-ns my-great-namespace.core)
(require 'some-other-ns.a.b :as 'ab)
(require 'some-other-other=ns.c.d :as 'cd)
The require reference also allows you to refer names, for example:
(ns my-great-namespace.core
(:require [some-other-ns.a.b :refer [some-func]]))
Where in function you should do:
(in-ns my-great-namespace.core)
(require 'some-other-ns.a.b)
(refer 'some-other-ns.a.b :only ['some-func])