Reading — step 1 of 6
Learn
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/3for 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 List | Map | |
|---|---|---|
| Keys | Atoms only | Any |
| Order | Preserved | Not preserved (most ops) |
| Duplicates | Allowed | Not allowed |
| Pattern matching | Strict on order | Order-independent |
| Lookup | O(n) | O(log n) |
| Use case | Function options | Data 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/1if 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. UseKeyword.get/2for 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…