Namespaces and requires
BasicA namespace is Clojure's unit of modularity. It groups vars (defined
with def, defn, …) under a common qualified name and controls how other
namespaces see them. The file src/myapp/core.clj is expected to begin with
(ns myapp.core ...) — file path and namespace name are mirrored, with -
in filenames becoming _ on disk (myapp-utils → myapp_utils.clj).
The ns form
(ns myapp.users
(:require [clojure.string :as str]
[clojure.set :refer [union]]
[myapp.db :as db])
(:import [java.time Instant LocalDate]))
(defn touch [u]
(assoc u :seen-at (Instant/now)))
Three blocks inside ns:
:requirepulls in Clojure namespaces.:importpulls in Java classes.- (Less common today:
:use,:refer-clojure,:gen-class.)
Inside :require, three modes:
| Form | Effect | When |
|---|---|---|
[clojure.string :as str] | Alias only; access via str/join | Default — keeps origin visible |
[clojure.set :refer [union difference]] | Pull specific names into your ns | Heavily-used operators or is, deftest in tests |
[my.lib :refer :all] | Pull everything | Almost never — pollutes your ns |
The pragmatic default is :as aliases. :refer is reserved for short,
canonical names that read better unqualified (<!, >!, is, testing).
Qualified vs unqualified
Every var has a fully-qualified name:
clojure.string/join ;; full name
str/join ;; via the alias above
join ;; only if you `:refer`red it
This matters because keywords can also be qualified — ::user/id
expands to :myapp.users/id in the current namespace. Qualified keywords
are essential for clojure.spec, Datomic, and any cross-namespace map
contract.
Loading and reloading at the REPL
Inside a running REPL you don't write ns forms — you require:
(require '[myapp.users :as users])
(require '[myapp.users :as users] :reload) ;; pick up file edits
(require '[myapp.users :as users] :reload-all) ;; reload transitive deps
Editors send these for you when you save a file. The reloaded namespace
replaces the previous one — any def you removed from source still
lingers in the live ns until you restart or use tools.namespace's
refresh.
File-to-namespace mapping
| Namespace | File on classpath |
|---|---|
myapp.core | myapp/core.clj |
myapp.users.db | myapp/users/db.clj |
my-app.core | my_app/core.clj (dashes → underscores) |
The classpath is set by [[tools-deps]] / Leiningen; the namespace declaration inside the file must match the path.
ClojureScript and .cljc
.clj→ Clojure (JVM) only..cljs→ ClojureScript only..cljc→ shared. Uses reader conditionals:#?(:clj ... :cljs ...).
Most modern libraries publish their public API as .cljc and dispatch
host-specific code internally.
Try it
Common pitfalls
- Circular requires. A requires B requires A. Restructure or extract a third namespace with shared definitions.
- Side-effects at the top level. A
(def conn (db/connect ...))at ns load time will fire whenever the namespace is required. Use [[component-systems]] for anything stateful. useinstead ofrequire + :refer.useis legacy; everything you bring in becomes a name in your ns, and you lose the origin. Prefer explicit:refer [...]lists.- Forgetting
:as.(:require [very.long.namespace.path])requires you to typevery.long.namespace.path/fooevery time. Alias it.
Real-world
| Pattern | Where |
|---|---|
| One namespace per "thing" (entity, route, service) | Web apps, CRUD systems |
core ns as the public API; helpers in impl/* | Libraries (e.g. clojure.core.async) |
.cljc for shared schema/utility code | Full-stack apps (Clojure backend + CLJS frontend) |
| Qualified keywords per ns | Datomic, [[malli]], [[clojure-spec]] schemas |
tools.namespace/refresh | Reloaded workflow in long-running dev REPLs |
Check yourself
? quiz
You require `[clojure.string :as str]`. Which name reaches `clojure.string/upper-case`?
Exercise
Create two namespaces:
myapp.mathexportingsquareandcube.myapp.mainthat requiresmyapp.mathwith aliasmand uses both functions.
Then sketch the same project with the math functions split into
myapp.math.basic and myapp.math.advanced. Decide: should myapp.main
require both, or should myapp.math re-export them from a single facade
namespace?