The issue here is subtle and possibly difficult to grok without first understanding a bit about macros.
Macros manipulate syntax in the same way that functions manipulate values. In fact, macros are just functions with a hook that causes them to be evaluated at compile time. They are passed the data literal that you see in the source code and are evaluated top-down. Let's make a function and a macro that have the same body so you can see the difference:
(defmacro print-args-m [& args]
(print "Your args:")
(prn args))
(defn print-args-f [& args]
(print "Your args:")
(prn args))
(print-args-m (+ 1 2) (str "hello" " sir!"))
; Your args: ((+ 1 2) (str "hello" " sir!"))
(print-args-f (+ 1 2) (str "hello" " sir!"))
; Your args: (3 "hello sir!")
Macros are replaced by their return value. You can inspect this process with macroexpand
(defmacro defmap [sym & args]
`(def ~sym (hash-map ~@args))) ; I won't explain these crazy symbols here.
; There are plenty of good tutorials around
(macroexpand
'(defmap people
"Steve" {:age 53, :gender :male}
"Agnes" {:age 7, :gender :female}))
; (def people
; (clojure.core/hash-map
; "Steve" {:age 53, :gender :male}
; "Agnes" {:age 7, :gender :female}))
At this point, I should probably explain that ' causes the following form to be quoted. This means that the compiler will read the form, but not execute it or try to resolve symbols and so forth. i.e. 'conj evaluates to a symbol, while conj evaluates to a function. (eval 'conj) is equivalent to (eval (quote conj)) is equivalent to conj.
With that in mind, know that you can't resolve a symbol as a namespace until it has been magically imported into your namespace somehow. This is what the require function does. It takes symbols and finds the namespaces they correspond to, making them available in the current namespace.
Let's see what the ns macro expands to:
(macroexpand
'(ns sample.core
(:require clojure.set clojure.string)))
; (do
; (clojure.core/in-ns 'sample.core)
; (clojure.core/with-loading-context
; (clojure.core/refer 'clojure.core)
; (clojure.core/require 'clojure.set 'clojure.string)))
See how it quoted the symbols clojure.set and clojure.string for us? How convenient! But what's the deal when you use require in stead of :require?
(macroexpand
'(ns sample.core
(require clojure.set clojure.string)))
; (do
; (clojure.core/in-ns 'sample.core)
; (clojure.core/with-loading-context
; (clojure.core/refer 'clojure.core)
; (clojure.core/require 'clojure.set 'clojure.string)))
It seems that whoever wrote the ns macro was nice enough to let us do it both ways, since this result is exactly the same as before. Neato!
edit: tvachon is right about only using :require since it is the only officially supported form
But what's the deal with brackets?
(macroexpand
'(ns sample.core
(:require [clojure.set]
[clojure.string])))
; (do
; (clojure.core/in-ns 'sample.core)
; (clojure.core/with-loading-context
; (clojure.core/refer 'clojure.core)
; (clojure.core/require '[clojure.set] '[clojure.string])))
Turns out they get quoted too, just like we'd do if we were writing standalone calls to require.
It also turns out that ns doesn't care whether we give it lists (parens) or vectors (brackets) to work with. It just sees the arguments as sequences of things. For example, this works:
(ns sample.core
[:gen-class]
[:require [clojure.set]
[clojure.string]])
require, as pointed out by amalloy in the comments, has different semantics for vectors and lists, so don't mix those up!
Finally, why doesn't the following work?
(ns sample.core
(:require 'clojure.string 'clojure.test))
Well, since ns does our quoting for us, these symbols get quoted twice, which is semantically different from being quoted only once and is also pure craziness.
conj ; => #<core$conj clojure.core$conj@d62a05c>
'conj ; => conj
''conj ; => (quote conj)
'''conj ; => (quote (quote conj))
I hope this helps, and I definitely recommend learning how to write macros. They're super fun.