A jonnify course
Functional Programming
in Elixir
A bridge from the algebra you already know to real-time systems on the BEAM.
Seven chapters, 59 modules, plus an optional history chapter. Every idea is defined precisely, paired with its Elixir form, and shown — not only described. The code is idiomatic and runnable; the diagrams compute the real thing.
How to read this
This course makes one claim and then earns it: functional programming is the algebra of functions, and Elixir is a place to run that algebra at scale. Each chapter takes an idea you already trust from mathematics and shows the line that connects it to working code.
The shape is deliberate. Algebra establishes the mindset; functional programming studies the tools on their own terms; the Elixir language gives them a syntax; algorithms put them under load; pragmatic engineering ships them; and Phoenix carries them onto the web and into real-time. Each chapter reuses the one before it, so nothing arrives without its foundation.
Pick a chapter from the arc below to see what it covers and what it stands on. The full module directory follows.
The arc · select a chapter
The course is one curve, not eight disconnected topics. Read it in order and each chapter is a consequence of the last.
One idea, early: composition
The thread that runs through every chapter is composition. In algebra, applying f
and then g is the single function g∘f. In Elixir, that
same act has a syntax — the pipe |> — which reads left to right in the order the
work happens.
Three pure functions are wired below: double maps x to 2x, increment adds one, and square maps x to x². Set the input, toggle which functions take part, and watch the pipeline and its result rebuild.
The pipe · build a pipeline
3 |> double() # => 6 |> increment() # => 7 |> square() # => 49
A pipeline reads left to right: x |> f |> g is
g(f(x)). Turn every function off and the pipeline is the identity — it returns its
input untouched. F1.03 makes f∘g and its associativity precise.
The map
The full course, chapter by chapter. Cards that link are ready to read; the rest are specified and on the way. The .09 of every chapter is a hands-on, richly visual lab.
The evolution of functional languages & runtimes
λ-calculus → LISP → ML/Haskell → the immutable turn.
- F0.1.1From λ-calculus to LISP
- F0.1.2Types & laziness — the ML and Haskell branch
- F0.1.3The immutable turn — persistent data on the JVM & CLR
The evolution of Erlang, the BEAM & OTP
Telecom roots, soft-real-time scheduling, and supervision.
- F0.2.1Telecom roots & “let it crash”
- F0.2.2Inside the BEAM — scheduling, heaps & soft-real-time GC
- F0.2.3OTP & the supervision tree — and the polyglot BEAM
What a function really is
Mapping, domain & range, exactly one output → first-class functions.
The substitution model
Equals for equals → referential transparency and purity.
Composition, f∘g
Chaining mappings, associativity → the pipe.
Immutability & binding
A symbol names a fixed value → immutable data.
Sets, sequences & mappings
Applying f across a collection → lists, maps, Enum.map.
Recursion & induction
Base case + step, and the proof → recursion, no loops.
Higher-order operators (Σ, Π)
Operators over functions → map / filter / reduce.
Equations & pattern matching
Identities, solving by structure → pattern matching.
Functions on the plane — a plotting lab
Plot and compose functions; watch f∘g as curves.
Functional Programming
Open chapter →Pure functions, immutability, and higher-order functions on their own terms.
Pure functions & side effects
What purity buys; isolating effects.
Immutability & persistent data
Structural sharing; why copying is cheap.
Higher-order functions
Functions as arguments and return values.
Recursion patterns & tail calls
Accumulators and tail-call optimisation.
map / filter / reduce (folds)
reduce as the universal fold.
Closures & partial application
Capturing environment; & and currying by hand.
Algebraic data types
Sum and product types; tagged tuples.
Composition & pipelines
Building programs by composing functions.
The data-pipeline lab
Compose map/filter/reduce over a dataset; watch each stage.
Values, types & IEx
The data you build with; the shell as a tool.
Pattern matching & the match operator
= is a match, not assignment.
Functions, modules & the pipe
Defining and composing in modules.
Enumerables & streams
Eager versus lazy traversal.
Structs, maps & keyword lists
Shaping data; when to use which.
Protocols & behaviours
Polymorphism and contracts.
Processes & the actor model
spawn, send, receive; isolation.
OTP: GenServer & supervisors
Stateful servers and fault tolerance.
The process playground
Spawn processes, send messages, watch the mailbox live.
Algorithms & Data Structures
Open chapter →Classical and advanced problems, from lists to branded CHAMP tries.
Lists, recursion & complexity
Cons cells; big-O on the BEAM.
Trees & traversals
Binary and n-ary trees; DFS/BFS, functionally.
Sorting & searching
Merge/quick sort and binary search, immutably.
Maps, sets & hashing
Hash maps, collisions, the cost model.
Hash Array Mapped Tries (HAMT)
Persistent maps via prefix trees.
CHAMP maps
Compressed Hash-Array Mapped Prefix-trees; layout & iteration.
Identifiers, Snowflake & branded ids
From naive ids to a Snowflake bigint and a branded, base62 id.
Branded ids & persistence
Branded ids as keys in SQLite, PostgreSQL, and Redis.
Branded CHAMP maps & GenServer
A CHAMP keyed by branded ids, partitioned by namespace, behind a GenServer.
Practical recipes in Elixir
Turning algorithmic problems into idiomatic Elixir.
Dynamic programming & advanced problems
Overlapping subproblems, memoized and tabulated.
Lab: build a branded CHAMP store
An interactive lab: insert branded keys and watch the partitioned CHAMP restructure.
Pragmatic Programming
Open chapter →Real-world engineering: structure, testing, telemetry, releases.
Foundations
Start thin: a running Portal from day one.
Modeling the Portal domain
Bounded contexts, structs, and the public API.
Tracer bullets: a walking skeleton
Thin end-to-end first, then iterate.
Design by contract
Preconditions, postconditions, and failing fast.
Commands, queries & events
Separate writes from reads; the engine as a reducer over events.
Where engine state lives
One process holds the state; one supervisor keeps it alive.
Pragmatic testing
Testing the pure core, property-based tests, and contracts as tests.
Boundaries & integration seams
Ports out, a facade in, one error vocabulary for the UI.
Lab: the Portal engine, LiveView-ready
Assemble the engine end to end, then mount it behind a LiveView.
Architecture & the request lifecycle
endpoint → router → controller → view.
Routing, controllers & plugs
The plug pipeline.
Ecto: schemas, changesets & queries
Data, validation, the repo.
Contexts & domain design
Boundaries that scale.
Templates, components & HEEx
Server-rendered markup.
Phoenix LiveView fundamentals
Interactive UIs without hand-written JS.
PubSub, channels & real-time
Live updates over WebSockets.
Auth, deployment & going live
Sessions, releases, production.
The live dashboard
Real-time LiveView state over a socket; multi-client via PubSub.
Deploy to Fly
Open chapter →Take the Portal live on Fly.io — the four stacked, silent-until-boot deploy gotchas, mastered one module at a time.
The release image
A multi-stage Dockerfile that builds the scoped release and ships every file the runtime needs.
Runtime config & the IPv6 network
runtime.exs at boot; Postgrex over Fly’s IPv6 DNS.
The database goes live
A reachable, migrated Postgres behind the F5 port.
fly.toml & the deploy mechanics
A distilled config; a deploy that rolls and self-heals.
Portal is live
The verified-live endpoint and the day-2 operations.