Skip to content
Atoms and Keyword Lists
step 1/6

Reading — step 1 of 6

Learn

~3 min readAtoms, Anonymous Functions, Comprehensions

Atoms and keyword lists are everywhere in idiomatic Elixir. The Elixir docs treat them as foundational. You'll meet them in every module, every function call.

Atoms

Atoms are constants where the name IS the value. Like Ruby symbols or Lisp symbols:

:ok
:error
:hello
:"with spaces or punctuation!"     # quoted form for special chars

Atoms are unique by name. :ok == :ok is always true — they're the same internal value.

Common uses:

# Status tags in tuples (the canonical Elixir return pattern):
{:ok, value} = parse(input)
{:error, reason} = parse(bad_input)

# Map keys (often preferred over strings for known fields):
user = %{name: "Ada", age: 36}     # %{name: ...} is sugar for %{:name => ...}

# Module names (modules ARE atoms):
is_atom(String)                     # true

Boolean values true, false, and nil are actually atoms. So is every Module name.

Atom comparisons

:hello == :hello         # true — same atom
:hello == "hello"        # false — different types
:apple < :banana         # true — atoms compare lexically by name

Atom comparison is fast — they're internally just integer references to the atom table.

Caveat: atoms are NOT garbage-collected. Don't dynamically create unbounded numbers of them (e.g., from user input) — you'll exhaust the atom table.

Keyword lists

A keyword list is a list of {atom, value} tuples where the keys are atoms:

opts = [name: "Ada", age: 36, role: :admin]
# Equivalent to:
opts = [{:name, "Ada"}, {:age, 36}, {:role, :admin}]

The [name: "Ada", ...] syntax is sugar.

Properties:

  • Ordered (preserve insertion order)
  • Allow duplicate keys
  • Use Keyword.get/3 for lookup
Keyword.get(opts, :name)            # "Ada"
Keyword.get(opts, :missing, :default)
Keyword.put(opts, :age, 37)

Keyword lists in function calls

The most common use — trailing keyword lists are syntactic sugar:

# These two are equivalent:
String.split("a,b,c", ",", trim: true)
String.split("a,b,c", ",", [trim: true])

When the keyword list is the LAST argument, you can drop the brackets. This is why Elixir API calls look so clean:

Ecto.Repo.all(query, prefix: "public", timeout: 10_000)
Plug.Conn.send_resp(conn, 200, body, headers: [...], cache: false)

Keyword lists vs maps

Similar but different:

Keyword ListMap
KeysAtoms onlyAny
OrderPreservedNot preserved (most ops)
DuplicatesAllowedNot allowed
Pattern matchingStrict on orderOrder-independent
LookupO(n)O(log n)
Use caseFunction optionsData records

Rule of thumb:

  • Function options → keyword list
  • Domain data → map (or struct)
  • Configuration → keyword list (when ordered) or map

Common mistakes

  • Creating atoms from user input — exhausts atom table; can DoS your VM. Use String.to_existing_atom/1 if you must convert strings to atoms.
  • Mixing keyword list and map syntax%{name: ...} is a map; [name: ...] is a keyword list. Different types.
  • Expecting keyword-list lookup to be fast — it's O(n). For hot lookups, use a map.
  • Pattern matching on keyword lists with different order — exact match: [name: n, age: a] matches only that exact order. Use Keyword.get/2 for order-independent access.
  • Atoms vs strings in map keys — pick one and stick to it. %{name: ...} (atom keys) is conventional for internal data; %{"name" => ...} (string keys) for JSON-derived data.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…