There are several introductory Haskell books, each with their own strengths and weaknesses. I have tried a few of them, but none really clicked with me except Get Programming with Haskell. This is the introductory Haskell book that I would recommend to other software engineers. If you are a practicing software engineer who wants a quick introduction covering just the right amount of topics to write a weekend project in Haskell, this is the book for you.
This book stands out for its emphasis on pragmatism. All topics are introduced as tools for getting something done with minimal or no theory, in contrast to most Haskell books that delve deeply into theory from the outset. For example, consider how several introductory Haskell books approach the very first topic: functions.
Haskell Programming from First Principles covers lambda calculus in detail in the very first chapter before even discussing how to install Haskell. Then, in the next chapter, it discusses what expressions are and explains that functions are a specific type of expression. Programming in Haskell starts with a very formal definition of a function: “In Haskell, a function is a mapping that takes one or more arguments and produces a single result, and is defined using an equation that gives a name for the function, a name for each of its arguments, and a body that specifies how the result can be calculated in terms of the arguments.” Thinking Functionally with Haskell is aimed at learning programming using Haskell, not the language itself, and starts the book by discussing how to represent mathematics in Haskell. In contrast, Get Programming with Haskell says that functions in Haskell work just like in mathematics. Instead of talking about expressions formally, it simply mentions that there’s no need to write an explicit return statement because all Haskell functions must return a value.
Another example is the discussion about monoids. Most Haskell books start with the definition: a monoid is a binary associative operation with an identity, followed by a discussion of what each term - binary, associative, operation, identity - means and how monoid works. Get Programming with Haskell, in contrast, leads in with the programmers’ needs to combine two similar things and introduces monoid as a tool to solve that problem.
The focus on pragmatism is also reflected in the order of topics. IO is introduced very early in the book, in lesson 21 on page 249 out of 42 lessons. And yes, that’s very early in Haskell. As the author says, “the most difficult part of learning (and teaching) Haskell is that you need to cover a fairly large number of topics before you can comfortably perform even basic I/O.” Haskell Programming from First Principles delves deep into IO in the final chapter 29 on page 1059. Thinking Functionally with Haskell covers it in chapter 10 on page 239 out of 12 chapters.
In addition, the book uses practical examples. While the first two capstone examples are trivial, the subsequent ones feel like something I might have to deal with at work. These include performing calculations on time series data, processing binary data from a library book record format called MARC records, writing a clone of LINQ from C# called HINQ for interacting with databases, and creating a Haskell project for finding prime numbers including some property-based tests. The very last lessons cover other real-world tasks such as error handling, making HTTP requests and handling responses, encoding and decoding JSON data, interacting with a sqlite3 database, and using mutable arrays to implement algorithms requiring in-place mutation.
Of course, the book is not perfect. Firstly, its scope is narrower than other books. While I really appreciate that the author introduced key practical concepts like interfacing with a database or using stateful arrays, which enable writing simple projects, you need more to write more complex projects. That includes a deeper understanding of topics that the author skimmed over, such as operator precedence or defining and using types and typeclasses, and topics that were not covered, such as reader monads or monad transformers.
Secondly, skipping theory prevents thoroughly understanding the concepts covered in the book. The book explains how to do things, but not why things work that way. I mentioned that Haskell Programming from First Principles covers lambda calculus in its very first chapter. I disagree with the choice to begin the book with it, but I also acknowledge that it provides a comprehensive mental model with which you can understand Haskell’s design and inner workings. Without a theoretical background, what you learn from Get Programming with Haskell can feel like a loose collection of recipes rather than an encompassing understanding of the language.
Thirdly, avoiding theoretical terms hampers further learning of Haskell. Do you remember the definition of monoids? Most books start with its definition because that’s actually how the Haskell community engages in discussion. Look at the documentation for the monoid typeclass. Few other languages would start by talking about associativity and identity. Even error messages will throw alien technical jargon at you, like skolem variable. Clearly, I’m not the only one bummed out by that error message. But unlike communities for other languages such as Elm, Rust, or even TypeScript, the Haskell community doesn’t have a community-wide drive toward making things easier to understand. So you have to eventually pick up those theoretical terms to go further in Haskell, even if you were spared from their onslaught in Get Programming with Haskell.
Despite its flaws, I still recommend this as the best introductory Haskell book for most software engineers. The book teaches you Haskell’s syntax and basic concepts in terms of software engineering that you are familiar with, in contrast to other books that teach Haskell in terms of theoretical computer science and mathematics that you may not have learned or may have left behind in your college days. Once you have crossed that bridge into Haskell land, you can more easily pick up theory through other books.
In the preface, the author states his goal: “I’ve always wanted to read a book that shows you how to solve practical problems that are often a real pain in Haskell. I don’t particularly care to see large, industrial-strength programs, but rather fun experiments that let you explore the world with this impressive programming language. I’ve also always wanted to read a Haskell book that’s reasonably short and that, when I’m finished, enables me to feel comfortable doing all sorts of fun weekend projects in Haskell.” I believe that the author has achieved his goal and more. If you want to learn Haskell and are the right target audience, then this is the book for you.