Core Syntax and Data Types at a Glance
Elixir's core data types are integers and floats (arbitrary-precision integers, no overflow), atoms (constants whose name is their own value, written :ok or :error), booleans (which are actually just the atoms true/false), binaries/strings (UTF-8 encoded, double-quoted), lists ([1, 2, 3], linked-list under the hood), tuples ({:ok, 42}, fixed-size and index-fast), and maps (%{name: "Ada", age: 30}, key-value with average O(1) access). Variables are bound with = (which is actually pattern matching, not assignment), and both modules and functions are defined with defmodule/def, where every function's arity (its argument count) is part of its identity, so greet/1 and greet/2 are considered entirely different functions that happen to share a name.
Cricket analogy: An atom like :ok is like a fixed cricketing term such as 'LBW' -- the word itself is the entire meaning, unlike a variable score that changes every ball; a function's arity mattering is like how 'over' means something completely different in a 6-ball over count versus 'run over,' context (argument count) changes identity.
Collections: Lists, Tuples, and Maps
Lists are linked lists -- prepending with [0 | list] is O(1), but indexing or appending to the end is O(n), so lists are best for sequential processing and pattern matching ([head | tail]), not random access. Tuples are fixed-size and stored contiguously, so elem(tuple, 0) is O(1), making them ideal for small, fixed-shape data like function return values ({:ok, result} / {:error, reason}) where the shape never changes at runtime. Maps offer average O(1) key lookup and are updated either with Map.put(map, :key, value) (works even if :key doesn't exist yet) or the more restrictive %{map | key: value} update syntax, which is faster but raises a KeyError if :key isn't already present -- a subtle but important distinction.
Cricket analogy: A list is like a batting order card where you can quickly slot a new opener at the top (O(1) prepend) but finding out who's batting at number 9 means reading down the whole card sequentially (O(n)); a tuple is like a fixed three-result scoreboard (win/loss/tie) where any slot is instantly readable.
The Pipe Operator and Enum/Stream Basics
The pipe operator |> takes the expression on its left and inserts it as the first argument of the function call on its right, so data |> Enum.filter(&(&1 > 0)) |> Enum.map(&(&1 * 2)) reads top-to-bottom as a data transformation pipeline instead of nesting Enum.map(Enum.filter(data, ...), ...) inside-out. Enum functions are eager -- each step in a pipeline fully processes the entire collection and builds an intermediate list before the next step runs -- while Stream functions are lazy, building up a description of the transformations that only actually runs when a terminal function like Enum.to_list/1 or Enum.take/2 pulls values through, which matters a lot for large or infinite sequences (Stream.iterate/2, Stream.cycle/1) where materializing the whole thing eagerly would be wasteful or impossible.
Cricket analogy: The pipe operator is like a fielding relay drill -- catch, throw, catch, throw -- read left to right, rather than a nested description of who caught whose throw; Stream's laziness is like a bowler only running in to bowl the specific delivery the captain calls, not pre-bowling every possible delivery in advance.
# Core types
i = 42
f = 3.14
atom = :ok
str = "hello"
list = [1, 2, 3]
tuple = {:ok, 42}
map = %{name: "Ada", age: 30}
# Map update syntax
map2 = Map.put(map, :city, "London") # adds or updates :city
map3 = %{map | age: 31} # requires :age to already exist
# Module and function definition
defmodule Greeter do
def greet(name), do: "Hello, #{name}!"
def greet(name, greeting), do: "#{greeting}, #{name}!"
end
# Pipe + Enum
[1, -2, 3, -4, 5]
|> Enum.filter(&(&1 > 0))
|> Enum.map(&(&1 * 2))
|> Enum.sum()
# => 18
# case / cond
case Greeter.greet("Ada") do
"Hello, " <> _ -> :greeted
_ -> :unknown
endStart iex -S mix inside a project to get a REPL with your app's modules loaded; typing h Enum.map inside iex prints that function's documentation directly, and i value inspects any value's type and protocol implementations -- both are faster than searching docs in a browser while learning.
== performs value comparison and treats 1 == 1.0 as true (numeric equality across int/float), while === (strict comparison) treats 1 === 1.0 as false because the types differ. This distinction has bitten many newcomers when comparing IDs or amounts fetched from different sources -- one as an integer, one as a float -- and getting an unexpected match with ==.
Common Standard Library Cheat Points
The String module operates on UTF-8 binaries and is grapheme-aware (String.length/1 counts user-perceived characters correctly even for multi-byte scripts, unlike byte_size/1), with common functions like String.split/2, String.trim/1, String.replace/3, and String.upcase/1 covering the vast majority of text-processing needs. A frequent beginner confusion is when to reach for Enum versus List: Enum works polymorphically over anything implementing the Enumerable protocol (lists, maps, ranges, streams), while List contains functions specific to list structure like List.flatten/1 or List.first/1 -- in practice, Enum is the default choice unless you need a list-specific operation. On the tooling side, mix new my_app scaffolds a project, mix deps.get fetches dependencies declared in mix.exs, mix test runs ExUnit, and mix format auto-formats code to the community-standard style.
Cricket analogy: String.length/1 counting graphemes correctly is like a scorer correctly counting a player's actual name (say, a name with diacritics) as intended rather than miscounting based on raw byte encoding; Enum's polymorphism is like a single scoring system that works whether you're tracking a Test match, ODI, or T20 without needing a separate scorer for each format.
- Elixir's core types include integers/floats, atoms, booleans (true/false atoms), UTF-8 binaries/strings, lists, tuples, and maps.
- Function identity includes arity -- greet/1 and greet/2 are entirely different functions even with the same name.
- Lists are linked lists (fast prepend, O(n) index/append); tuples are fixed-size with O(1) indexed access; maps offer average O(1) key lookup.
- Map.put/3 adds or updates a key even if absent; the %{map | key: value} syntax is faster but raises KeyError if the key doesn't already exist.
- The pipe operator |> threads a value as the first argument into the next function call, reading top-to-bottom instead of nesting calls inside-out.
- Enum functions are eager and materialize intermediate collections; Stream functions are lazy and only evaluate when pulled by a terminal function.
- == allows cross-type numeric equality (1 == 1.0 is true); === is strict and requires matching types (1 === 1.0 is false).
Practice what you learned
1. What does the = operator actually do in a statement like x = 5?
2. Which collection type gives O(1) indexed access due to being stored contiguously?
3. What is the key difference between Map.put/3 and the %{map | key: value} syntax?
4. What is the main difference between Enum and Stream functions?
5. What does 1 === 1.0 evaluate to in Elixir, and why?
Was this page helpful?
You May Also Like
Elixir vs Ruby
A practical comparison of Elixir and Ruby covering syntax, concurrency, fault tolerance, mutability, and when to reach for each language.
Elixir Best Practices
Idiomatic conventions and patterns for writing maintainable, robust Elixir code, from naming and pattern matching to OTP and testing.
Building an API with Phoenix
A practical walkthrough of building a JSON API in Phoenix -- routing, controllers, Ecto contexts, JSON rendering, and testing.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics