Browse Learn Clojure Foundations as a Java Developer

Symbols vs Keywords in Clojure

Choose symbols for code-facing names and keywords for data-facing labels, with Java comparisons, map examples, review heuristics, and common migration mistakes.

If this subsection only gives you one lasting habit, make it this:

Symbols are resolved names. Keywords are literal data labels.

That distinction makes Clojure syntax much easier to read.

Side-by-Side

Question Symbol Keyword
Does it usually evaluate to itself? No Yes
Does it usually participate in name lookup? Yes No
Does it usually name code? Yes No
Does it usually label data? No Yes
Common examples user, map, str/trim :user/id, :active
Java analogy Local name, method reference, class name Field label, enum value, constant token

Example:

1(defn active-emails [users]
2  (->> users
3       (filter #(= :active (:user/status %)))
4       (map :user/email)))

In this form:

  • defn, active-emails, users, filter, map, and = are symbols.
  • :active, :user/status, and :user/email are keywords.

The symbols tell Clojure what code to run. The keywords describe the data.

Evaluation Difference

A symbol resolves:

1(def threshold 10)
2
3threshold
4;; => 10

A keyword evaluates to itself:

1:threshold
2;; => :threshold

Quoted symbols are the exception because quoting stops resolution:

1'threshold
2;; => threshold

Java Comparisons That Help

Java comparison is useful but imperfect:

Java habit Clojure equivalent tendency
Local variables and parameters Symbols
Method or static references Symbols or qualified symbols
Field names in DTO-like data Qualified keywords
Enum constants Keywords
String constants for keys Keywords inside the Clojure core
Reflection over names Rare; usually macros or tooling

The biggest shift is that Clojure data is often plain maps. Keywords make those maps readable without creating classes just to hold fields.

Use Symbols For Code-Facing Names

Good symbol use cases:

  • function names
  • function parameters
  • let bindings
  • namespace-qualified var references
  • Java class names
  • quoted code forms for macros and tooling
1(require '[clojure.string :as str])
2
3(let [email "DEV@EXAMPLE.COM"]
4  (str/lower-case email))

email and str/lower-case are symbols because Clojure resolves them.

Use Keywords For Data-Facing Labels

Good keyword use cases:

  • map keys
  • statuses
  • modes
  • options
  • event types
  • qualified domain attributes
1{:event/type :user-registered
2 :user/id 42
3 :user/email "dev@example.com"}

Everything with a leading : is a literal data label.

Review Heuristic

When reviewing code, ask:

Review question Likely answer
Should this token be looked up in scope or a namespace? Symbol
Should this token stay as a literal value in data? Keyword
Is this a map key in internal domain data? Usually keyword
Is this a function, parameter, or local name? Symbol
Is this external JSON or HTTP data? String at boundary, keyword after normalization
    flowchart TD
	    A["Identifier-like token"] --> B{"Leading colon?"}
	    B -->|Yes| C["Keyword: data label"]
	    B -->|No| D{"Quoted?"}
	    D -->|Yes| E["Symbol as data"]
	    D -->|No| F{"Names code?"}
	    F -->|Yes| G["Symbol"]
	    F -->|No| H["Reconsider data shape"]

Common Migration Mistakes

Mistake Why it hurts Better choice
{user/id 42} instead of {:user/id 42} Tries to resolve user/id as a symbol Use a keyword map key
{"status" "active"} everywhere internally Stringly typed core data Normalize to {:status :active}
:email as a function parameter Keywords do not introduce locals Use email as the symbol, :email as the map key
Dynamically creating symbols for fields Adds metaprogramming where data lookup is enough Use keyword keys
Calling keyword on arbitrary input Can create unbounded retained keyword values Validate and map known values

Practice

  1. Mark each token in this form as symbol or keyword:
1(defn paid? [invoice]
2  (= :paid (:invoice/status invoice)))
  1. Rewrite this string-keyed Java-style map into Clojure internal data:
1{"userId" 42
2 "status" "active"}
  1. Explain why user/id and :user/id are not the same token.
  2. Write a boundary function that converts "active" to :active and rejects unknown statuses.

Key Takeaways

  • Symbols are resolved names.
  • Keywords are self-evaluating data labels.
  • Use symbols for code-facing names and keywords for data-facing labels.
  • Qualified keywords are a strong default for larger internal data models.
  • Keep strings at external boundaries and normalize deliberately inside the application.

Quiz: Symbols vs Keywords

### Which token is a keyword? - [x] `:user/id` - [ ] `user/id` - [ ] `user` - [ ] `str/trim` > **Explanation:** Keywords begin with `:` and evaluate to themselves. ### Which token is a qualified symbol? - [x] `str/trim` - [ ] `:str/trim` - [ ] `"str/trim"` - [ ] `::trim` > **Explanation:** `str/trim` is a symbol qualified by a namespace alias. ### What should you usually use for internal domain map keys? - [x] Keywords such as `:user/id` - [ ] Bare symbols such as `user/id` - [ ] Mutable Java fields - [ ] Arbitrary unvalidated strings > **Explanation:** Keywords are literal data labels and are a strong default for Clojure maps. ### What does a symbol usually do when evaluated? - [x] Resolves to a local, var, class, or other name meaning. - [ ] Always returns itself. - [ ] Becomes a keyword. - [ ] Creates a map. > **Explanation:** Symbols usually participate in name resolution. ### True or False: `user/id` and `:user/id` mean the same thing. - [ ] True - [x] False > **Explanation:** `user/id` is a qualified symbol that Clojure tries to resolve. `:user/id` is a keyword value.
Revised on Saturday, May 23, 2026