-
Notifications
You must be signed in to change notification settings - Fork 4
0017: Email Tutorials Haskell For Beginners – Exploring Algebraic Data Types in Haskell
- 17.1. Recap: Sum Types and Product Types
- 17.2. The
StatusType – A Simple Algebraic Data Type - 17.3. Counting Possible Values of
Status - 17.4. A More Complex Example: Type
Aas 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
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
TrueorFalseforBool. -
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.
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 payloadKey points:
-
Statusis a sum type because it has two constructors:SuccessFailure Int8
-
Successis a nullary constructor, meaning it takes no arguments. It represents a simple “everything went fine.” -
Failureis a constructor that takes anInt8value. ThisInt8is 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.
The lesson then asks: How many different values can Status represent?
The reasoning goes like this:
-
Successis nullary, so it corresponds to exactly 1 possible value. There is only one way to be “success”: justSuccess. -
Failure Int8can carry any Int8 value. SinceInt8has 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(forSuccess) -
+ 256(for theFailurecases) - =
257possible distinct values forStatus.
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.
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
| CObservations:
-
Ahas three constructors:A,B, andC, so it is definitely a sum type (several alternatives). -
But some of those constructors take multiple parameters:
-
A Bool Int8is a product ofBoolandInt8. -
B Int8 Int8 Int8 Boolis a product of threeInt8values and oneBool.
-
-
Cis 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.
The lesson then asks: How many different values can a value of type A take? We consider each constructor separately.
-
Boolhas 2 possible values:TrueorFalse. -
Int8has 256 possible values.
Because this is a product type (Bool and Int8), we multiply:
-
2 * 256 = 512possible values for theAconstructor.
So any value of the form A b n can be one of 512 distinct combinations.
Here we have a product of four components:
- Three
Int8values, each with 256 possibilities. - One
Bool, with 2 possibilities.
To find the total, we multiply all of them:
-
256 * 256 * 256 * 2 = 33,554,432possibleBvalues.
Again, this multiplication reflects the product nature of the constructor.
C is a nullary constructor, so it has exactly 1 possible value: just C.
Since A is a sum of these three constructions, we add the counts:
-
512(from constructorA) -
+ 33,554,432(from constructorB) -
+ 1(from constructorC) - =
33,554,945possible values of typeA.
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.
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
datadeclaration. - 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 V3Each 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.
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.
-
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.
BoolandInt8). The number of possible values is the product of the possibilities for each component. -
Nullary constructor A constructor that takes no arguments, such as
SuccessorC. 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.
-
Int8An 8-bit integer type, which can represent exactly 256 distinct values (from-128to127inclusive). -
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
SuccessorFailure, 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.
Bernard Sibanda is a global Technology Entrepreneur, Web3 and Software Consultant with a deep focus on Cardano Blockchain, Midnight and Community building.
Key Positions:
- Founder, CTO, Developer Advocate cohort #1, Fullstake Developer, Cardano Ambassador, Catalyst Project Manager, DREP-WIMS:
- Co-founder of ABL Tech and Cardano Africa Live
- EBU-certified Plutus Pioneer (Plutus/Haskell)
- Cohort #1 Plutus Pioneer Developer
- Catalyst Community Reviewer & Funded Projects Manager
-
DRep for WIMS-Cardano (ID:
drep1yguj8zu48n99pv70yl6ckzt9hdgjy8yjnlqs2uyzcpafnjgu4vkul) - Intersect Developer Advocate
- Intersect Committe Member 2025-2026
- Cardano Marketer,Promoter and blogger
- Cardano Open Source Contributor
- Cardano communities and events organizer and builder
- Cardano Ambassador for South Africa
Official links:
- Stablecoins Dex
- Coxygen Global Universities
- WIMS Cardano Global
- Cardano Africa Live
- WIMS Cardano Videos
- Cardano Smart Contract Videos
- Fullstack IT Consulting
Social links: