Discover / Haskell programming / Reading path

Learn Haskell: the best books to read in order

@codesherpaBeginner → Intermediate
6
Books
42
Hours
3
Stages
Not yet rated

This curriculum takes an intermediate programmer from Haskell's first principles through its most powerful abstractions — type classes, monads, and beyond. Each stage builds directly on the last: you first internalize the language's core model, then master its type system and abstractions, and finally tackle real-world patterns and advanced theory. Reading books within each stage in order is important, as earlier titles supply the vocabulary and intuition that later ones assume.

1

Foundations: Thinking Functionally

Beginner

Understand Haskell's syntax, pure functions, recursion, higher-order functions, and basic type system well enough to write small programs from scratch.

Study plan for this stage

Pace: 8–10 weeks, ~40–50 pages/day (mix of reading and active coding)

Key concepts
  • Pure functions and immutability: functions always return the same output for the same input with no side effects
  • Recursion as the primary control flow: replacing loops with recursive patterns and understanding base cases
  • Higher-order functions: map, filter, fold, and function composition as tools for abstraction
  • Type system fundamentals: type signatures, type inference, and how types guide program design
  • Pattern matching and list comprehensions: deconstructing data and expressing computations declaratively
  • Lazy evaluation: understanding how Haskell defers computation and enables infinite data structures
  • Monads and I/O basics: the do-notation and how Haskell handles side effects within a pure framework
You should be able to answer
  • What is a pure function, and why does Haskell enforce purity? How does this differ from imperative languages?
  • Write a recursive function to compute factorial and explain how the base case and recursive case work together.
  • Explain the difference between map, filter, and fold. When would you use each one?
  • What does a type signature like `(a -> b) -> [a] -> [b]` tell you about a function's behavior?
  • How does pattern matching simplify code compared to conditional statements? Provide an example.
  • What is lazy evaluation, and how does it allow Haskell to work with infinite lists?
  • Write a small program using do-notation that reads input, processes it with pure functions, and prints output.
Practice
  • Work through all exercises in Chapters 1–7 of 'Programming in Haskell' (syntax, types, function definitions, list comprehensions, recursive functions)
  • Implement the following from scratch: quicksort, a function to find the nth Fibonacci number, and a function to flatten a nested list
  • Write 5 small programs using map, filter, and fold on lists—e.g., double all numbers, keep only evens, sum a list
  • Refactor an imperative algorithm (e.g., bubble sort or linear search) into a recursive Haskell function and compare clarity
  • Complete the 'Learn You a Haskell' chapters on higher-order functions and function composition; implement custom versions of map and filter
  • Build a simple text-processing tool (e.g., word frequency counter, line reversal) using pattern matching and list operations
  • Write 3 programs that use lazy evaluation: one generating an infinite list, one using take to consume part of it, and one combining both
  • Create a small interactive program using do-notation: read user input, apply pure functions to transform it, and display results

Next up: Mastering these foundations—pure functions, recursion, higher-order functions, and the type system—equips you to tackle more advanced abstractions like custom data types, typeclasses, and monadic patterns, which form the backbone of real-world Haskell programs.

Programming in Haskell
Graham Hutton · 2007 · 184 pp

A concise, mathematically clean introduction to Haskell from first principles. Its brevity and precision make it the ideal first read for someone with programming experience who wants a no-nonsense foundation.

Learn You a Haskell for Great Good!
Miran Lipovača · 2011 · 400 pp

Follows Hutton perfectly by reinforcing the same concepts with a friendlier, example-rich style. Its visual, playful approach cements intuition for lists, pattern matching, and early type classes like Functor.

2

Core Mastery: Types, Type Classes, and Abstractions

Intermediate

Deeply understand Haskell's type system, algebraic data types, type classes (Functor, Applicative, Monad), and how to reason about programs using types.

Study plan for this stage

Pace: 8–10 weeks, ~40–50 pages/day (including active reading and code practice)

Key concepts
  • Algebraic Data Types (ADTs): sum types, product types, and pattern matching as the foundation for type-driven design
  • The Haskell type system: type inference, type signatures, polymorphism, and how types constrain program behavior
  • Type classes and their laws: Eq, Ord, Show, Functor, Applicative, and Monad as abstractions for common patterns
  • Functor abstraction: mapping over containers and understanding structure-preserving transformations
  • Applicative functors: sequencing computations with effects and the relationship between Functor and Applicative
  • Monads and do-notation: sequencing effectful computations, understanding bind (>>=), and practical monad instances
  • Reasoning about programs using types: leveraging the type system to guide implementation and catch errors at compile time
  • Abstraction through type classes: writing generic, reusable code that works across multiple types
You should be able to answer
  • How do sum types and product types differ, and how do you use pattern matching to deconstruct them safely?
  • Explain the Functor type class: what does fmap do, why does it preserve structure, and what are the functor laws?
  • What is the relationship between Functor, Applicative, and Monad? How does each add new capabilities?
  • Write and explain a custom Monad instance: what does bind (>>=) do, and how does it differ from Applicative's (<*>)?
  • How can you use the type system to guide implementation? Give an example where a type signature constrains the possible implementations.
  • What is do-notation, and how does it desugar into bind and return operations?
Practice
  • Define 3–4 custom algebraic data types (e.g., a tree, a linked list, an expression evaluator) and write exhaustive pattern matches for each
  • Implement Functor, Applicative, and Monad instances for a custom type (e.g., Maybe, a simple tree, or a custom effect type); verify they satisfy the laws
  • Refactor imperative-style code using do-notation: rewrite a sequence of operations with explicit bind calls, then convert to do-notation and verify equivalence
  • Write a small parser or interpreter using Monad abstractions (e.g., a simple expression parser with error handling via Maybe or Either)
  • Implement a generic function that works across multiple Functor or Monad instances; demonstrate polymorphism through type classes
  • Solve 10–15 type-driven exercises: given only a type signature, implement the function and explain why the type constrains the solution

Next up: Mastering types, type classes, and abstractions equips you to tackle advanced patterns like transformers, lens libraries, and domain-specific languages—all of which build on deep type-system reasoning and compositional abstractions.

Haskell Programming from First Principles
Christopher Allen · 2015

The most thorough ground-up treatment of Haskell available, with extensive exercises. Reading it after the foundation stage lets you move quickly through early chapters and slow down precisely where it matters — Functor, Applicative, Monad, and Foldable — building rock-solid intuition.

The Haskell School of Expression
Paul Hudak · 2000 · 382 pp

Uses multimedia and domain-specific languages as a vehicle to practice type-driven design and higher-order thinking. It bridges pure theory and applied programming, showing how elegant abstractions arise naturally from real problems.

3

Real-World Haskell: Effects, IO, and Production Patterns

Intermediate

Learn to write practical, production-quality Haskell: handle IO and side effects safely, work with common libraries, manage errors, and structure larger programs.

Study plan for this stage

Pace: 8–10 weeks, ~40–50 pages/day (Real World Haskell: 5–6 weeks; Effective Haskell: 3–4 weeks)

Key concepts
  • The IO monad and how it encapsulates side effects safely within Haskell's pure type system
  • Error handling patterns: Either, Maybe, exceptions, and when to use each in production code
  • Working with common libraries: bytestring, text, containers, and their performance characteristics
  • Lazy evaluation and its implications for resource management, streaming, and memory usage
  • Structuring larger programs with modules, type classes, and architectural patterns
  • Concurrency and parallelism: MVars, STM, and async for safe multi-threaded code
  • Testing strategies and property-based testing with QuickCheck
  • Debugging, profiling, and optimization techniques for production Haskell
You should be able to answer
  • Why is the IO monad necessary in Haskell, and how does it preserve referential transparency while allowing side effects?
  • When should you use Either vs. Maybe vs. exceptions for error handling, and what are the trade-offs?
  • How does lazy evaluation affect resource management, and what patterns help you avoid space leaks?
  • What are the key differences between bytestring and text, and when should you use each?
  • How do you structure a multi-module Haskell project, and what role do type classes play in managing complexity?
  • What are the main concurrency primitives in Haskell (MVars, STM, async), and how do they differ in their guarantees?
Practice
  • Build a command-line tool that reads a file, processes its contents (e.g., word count, filtering), and writes results to stdout or a new file, using proper error handling.
  • Implement a small web scraper or API client using HTTP libraries, handling network errors and parsing JSON responses gracefully.
  • Write a multi-threaded program (e.g., a simple worker pool or producer–consumer) using MVars or STM, and verify thread safety with concurrent access patterns.
  • Refactor a monolithic Haskell program into multiple modules with clear interfaces; use type classes to abstract over different implementations.
  • Create a property-based test suite for a utility function using QuickCheck; identify edge cases and ensure the function is robust.
  • Profile a Haskell program using GHC's built-in tools (e.g., +RTS -p), identify a space leak or performance bottleneck, and optimize it.

Next up: This stage equips you with the practical skills and architectural knowledge to build real, maintainable Haskell systems; the next stage will deepen your expertise in advanced type system features, performance optimization, and specialized domains (e.g., web frameworks, data processing) that leverage these foundations.

Real World Haskell
Bryan O'Sullivan · 2008 · 710 pp

The canonical reference for applied Haskell, covering parsing, IO, concurrency, testing, and databases. After mastering abstractions, this book shows how they are used at scale in real codebases.

Effective Haskell
Rebecca Skinner · 2022

A modern complement to Real World Haskell that focuses on idiomatic, maintainable code. It addresses contemporary tooling and patterns that have emerged since the older classic was written.

Discussion

Keep reading

Paths that share books, cover the same subject, or open a related topic.

Shares 3 books

Functional programming: a reading path to think in functions

Intermediate8books57 hrs3 stages
More on Scala programming

Learn Scala: the best books in order

Beginner6books43 hrs5 stages
More on Elixir programming

Learn Elixir: the best books to read in order

Beginner8books58 hrs4 stages
More on Clojure programming

Learn Clojure: the best books in order

Beginner8books59 hrs4 stages