Elixir inherits Erlang's functional patterns and adds powerful metaprogramming via macros. Today covers Enum and Stream for data processing, protocols for polymorphism, and macros that extend the language itself.
Enum processes collections eagerly — all at once. Stream processes lazily — one element at a time, on demand. Enum.map/filter/reduce/sort/group_by cover most data processing needs. For large collections or infinite streams, Stream.map/filter/take work the same way but only pull elements when needed. 'Stream.cycle([1,2,3]) |> Enum.take(10)' produces [1,2,3,1,2,3,1,2,3,1] without ever creating an infinite list in memory.
A Protocol defines an interface that different data types implement. Protocol.derive/2 can auto-derive implementations. Built-in protocols: Enumerable (makes a type work with Enum), Collectable (can receive elements), Inspect (custom iex display), String.Chars (to_string conversion). Define: 'defprotocol Serializable do; def serialize(term); end'. Implement: 'defimpl Serializable, for: MyStruct do...'.
Elixir macros run at compile time and generate AST nodes. defmacro defines a macro. quote/2 captures code as AST. unquote/1 injects values into quoted expressions. Macros are why Elixir can implement features like defmodule, def, if, and use as library code, not built-in syntax. Rule: use functions when possible; reach for macros only when you need to transform syntax or generate repetitive code.
# Enum vs Stream comparison
# Enum: eager, processes all at once
1..1_000_000
|> Enum.map(&(&1 * 2))
|> Enum.filter(&(rem(&1, 3) == 0))
|> Enum.take(5)
# Creates 1M intermediate list
# Stream: lazy, only computes what's needed
1..1_000_000
|> Stream.map(&(&1 * 2))
|> Stream.filter(&(rem(&1, 3) == 0))
|> Enum.take(5) # Triggers evaluation
# [6, 12, 18, 24, 30] -- O(n) not O(n^2)
# Protocol example
defprotocol Area do
def calculate(shape)
end
defimpl Area, for: %{type: :circle} do
def calculate(%{r: r}), do: :math.pi() * r * r
end
# Macro example: unless (inverse of if)
defmacro unless(condition, do: block) do
quote do
if !unquote(condition), do: unquote(block)
end
end
unless 1 == 2 do
IO.puts "1 is not 2"
end
Process a 1GB CSV log file using Stream: parse each line, filter for ERROR entries, extract the timestamp and message, group by hour, and write a summary report. Measure peak memory usage.