Skip to content

0017: Email Tutorials Haskell For Beginners – Exploring Algebraic Data Types in Haskell

Bernard Sibanda edited this page Dec 9, 2025 · 3 revisions

LigerLearn Video 17

📑 Table of Contents

  • 17.1. Recap: Sum Types and Product Types
  • 17.2. The Status Type – A Simple Algebraic Data Type
  • 17.3. Counting Possible Values of Status
  • 17.4. A More Complex Example: Type A as Both Sum and Product
  • 17.5. Counting Values for a Combined Sum-and-Product Type
  • 17.6. What Are Algebraic Data Types (ADTs)?
  • 17.7. Why Algebraic Data Types Make Your Code Better
  • 17.8. Glossary of Terms for This Lesson

17.1. Recap: Sum Types and Product Types

Before diving into algebraic data types, it helps to recall the two basic building blocks:

  • A sum type represents a value that can be one of several alternatives. Think: “this or that,” such as True or False for Bool.

  • A product type represents a value that combines several components at once. Think: “this and that,” such as a pair (Bool, Int) that holds both a Boolean and an integer together.

Algebraic data types are built by combining these two ideas—sums and products—in a structured way, using Haskell’s data keyword and multiple constructors.

17.2. The Status Type – A Simple Algebraic Data Type

The first example in the lesson defines a type called Status, which is meant to represent the result of some operation: it either succeeds or fails. In Haskell, it might look like this (simplified):

data Status
  = Success              -- nullary constructor
  | Failure Int8         -- constructor with an Int8 payload

Key points:

  • Status is a sum type because it has two constructors:

    • Success
    • Failure Int8
  • Success is a nullary constructor, meaning it takes no arguments. It represents a simple “everything went fine.”

  • Failure is a constructor that takes an Int8 value. This Int8 is used as a failure code, giving extra information about why things went wrong.

The lesson also notes that Int8 is a special integer type with only 8 bits of precision, so it can represent integers in the range -128 to 127, giving 256 distinct values.

Because Int8 is not in the Prelude by default, you must import it (typically from Data.Int) before using it.

17.3. Counting Possible Values of Status

The lesson then asks: How many different values can Status represent?

The reasoning goes like this:

  • Success is nullary, so it corresponds to exactly 1 possible value. There is only one way to be “success”: just Success.

  • Failure Int8 can carry any Int8 value. Since Int8 has 256 possible values, there are 256 distinct ways to represent failure (one for each error code).

Because Status is a sum type, the total number of possible Status values is the sum of possibilities from each constructor:

  • 1 (for Success)
  • + 256 (for the Failure cases)
  • = 257 possible distinct values for Status.

This example shows directly why such types are called sum types: the total number of possibilities is obtained by summing the possibilities of each branch.

17.4. A More Complex Example: Type A as Both Sum and Product

The next example is deliberately more contrived so that you can see both sum and product aspects in the same type. The type (simplified) looks like this:

data A
  = A Bool Int8
  | B Int8 Int8 Int8 Bool
  | C

Observations:

  • A has three constructors: A, B, and C, so it is definitely a sum type (several alternatives).

  • But some of those constructors take multiple parameters:

    • A Bool Int8 is a product of Bool and Int8.
    • B Int8 Int8 Int8 Bool is a product of three Int8 values and one Bool.
  • C is a nullary constructor, representing a single distinct value on its own.

This means that A is both:

  • A sum (many constructors), and
  • Inside each constructor, some are products (multiple fields).

This is a great example of an algebraic data type built out of sums of products.

17.5. Counting Values for a Combined Sum-and-Product Type

The lesson then asks: How many different values can a value of type A take? We consider each constructor separately.

1. Values created with constructor A Bool Int8

  • Bool has 2 possible values: True or False.
  • Int8 has 256 possible values.

Because this is a product type (Bool and Int8), we multiply:

  • 2 * 256 = 512 possible values for the A constructor.

So any value of the form A b n can be one of 512 distinct combinations.

2. Values created with constructor B Int8 Int8 Int8 Bool

Here we have a product of four components:

  • Three Int8 values, each with 256 possibilities.
  • One Bool, with 2 possibilities.

To find the total, we multiply all of them:

  • 256 * 256 * 256 * 2 = 33,554,432 possible B values.

Again, this multiplication reflects the product nature of the constructor.

3. Values created with constructor C

C is a nullary constructor, so it has exactly 1 possible value: just C.

4. Total number of possible values of A

Since A is a sum of these three constructions, we add the counts:

  • 512 (from constructor A)
  • + 33,554,432 (from constructor B)
  • + 1 (from constructor C)
  • = 33,554,945 possible values of type A.

This exercise makes it very clear where the names “sum type” and “product type” come from:

  • For a product, you multiply counts.
  • For a sum, you add counts.

An algebraic data type is built out of these two operations.

17.6. What Are Algebraic Data Types (ADTs)?

The lesson introduces the name algebraic data types for these kinds of definitions. In Haskell, an algebraic data type is:

A type defined by providing several alternatives, each with its own constructor.

More concretely:

  • You write a data declaration.
  • You give a type name.
  • You list one or more constructors separated by pipes (|).
  • Each constructor can take zero or more arguments, which themselves have types.

A generic template looks like:

data MyType
  = Constructor1 T1 T2
  | Constructor2 U1
  | Constructor3
  | Constructor4 V1 V2 V3

Each constructor is like a separate “shape” that values of MyType can take. The collection of these constructors, along with their fields, forms the algebraic data type.

They are called algebraic because:

  • They form an algebra of types using two primitive operations:

    • Sum (choice between alternatives)
    • Product (combining components together)

And as you saw, you can combine these operations to describe quite complex value spaces while still having a clean, compact syntax.

17.7. Why Algebraic Data Types Make Your Code Better

Haskell gives you a very lightweight syntax for defining new data types. Because it is so easy to define these rich, well-structured types, you are naturally encouraged to:

  • Model your problem domain explicitly and precisely.
  • Capture valid states and impossible states in the type system itself.
  • Use the compiler to enforce that you handle all the constructors (and thus all cases) of your types.

The result is a program that is:

  • More strongly typed – fewer invalid combinations can even be expressed.
  • More readable – constructor names and type names convey meaning.
  • More reliable – pattern matching and the type checker help ensure all possibilities are considered.

Algebraic data types are one of the core strengths of Haskell and many other functional languages, and they are central to writing clear, safe, and well-structured code.

17.8. Glossary of Terms for This Lesson

  • Sum type A type that represents a value that can be one of several alternatives. The number of possible values is the sum of the possibilities of each constructor.

  • Product type A type that combines multiple components together (e.g. Bool and Int8). The number of possible values is the product of the possibilities for each component.

  • Nullary constructor A constructor that takes no arguments, such as Success or C. It corresponds to exactly one possible value.

  • Constructor A named way of building values of a given type. Each constructor may take zero or more arguments.

  • Int8 An 8-bit integer type, which can represent exactly 256 distinct values (from -128 to 127 inclusive).

  • Algebraic Data Type (ADT) A type defined from other types using sums and products. It consists of one or more constructors, each with its own field types.

  • Tagged union (sum type with tags) A union where each alternative (constructor) is “tagged” with a name like Success or Failure, so the runtime knows which shape the value has.

  • Value space / number of possible values The set of all distinct values that a type can represent. For ADTs, computed from sums and products.

  • Type modeling Designing types that closely capture your domain concepts, so invalid states are hard or impossible to represent.

Quizz & Progress Badge NFT

Clone this wiki locally