-
Notifications
You must be signed in to change notification settings - Fork 4
0020: Email Tutorials Haskell For Beginners – Product Types and Tuples in Haskell
- 20.1. Cartesian Products in Mathematics
- 20.2. Product Types in Haskell
- 20.3. Tuples as Built-In Product Types
- 20.4. Modeling Data with Tuples: The Date Example
- 20.5. Limitations of Tuples for Real-World Data
- 20.6. Why Custom Types Are Better
- 20.7. Glossary of Terms
Before we talk about product types in Haskell, it helps to understand the idea of a Cartesian product from basic set theory.
Given two sets:
- A = {1, 2, 3}
- B = {1, 2, 3}
The Cartesian product A × B is the set of all ordered pairs formed by taking one element from A and one from B. If you lay them out in a grid, you get all combinations:
- (1,1), (1,2), (1,3)
- (2,1), (2,2), (2,3)
- (3,1), (3,2), (3,3)
Each cell in the table is one possible value of the pair. This idea of “all combinations of components” is what sits behind Haskell’s product types.
In Haskell, a product type is any type whose values are formed by combining values of other types into a single structure.
If you declare:
x :: (Int, Int)you are saying:
-
xis a pair ofIntvalues - Its possible values are all combinations
(a, b)whereaandbare validInts
Since Int has a fixed range, the total number of possible (Int, Int) values is the product of:
- the number of possible
Intvalues for the first component - times the number of possible
Intvalues for the second component
That is why they are called product types: the size of the type’s value space is the product of the sizes of the component types.
Haskell provides tuples as built-in product types. You have seen shapes like:
(Int, Int)
(Int, Double)
(String, Bool, Int)They are written with:
- Parentheses around the whole tuple
- Commas between elements
Example:
user :: (String, Int)
user = ("Alice", 30)This says:
-
useris a pair of aStringand anInt - The first position always holds a
String - The second position always holds an
Int
Tuples are convenient, quick, and anonymous product types; they don’t need a data definition.
To see the strengths and weaknesses of tuples, the video uses a date as an example.
You can represent a date like this:
date :: (Int, Int, Int)
date = (5, 10, 2025)This might mean:
-
5→ day -
10→ month -
2025→ year
But the type itself doesn’t say that. All the compiler sees is a triple of Ints.
This is where the limitations start to show.
While tuples work mechanically, they are not a great modeling tool for non-trivial data. The main problems are:
The type:
(Int, Int, Int)does not say “this is a date”. It just says “three Ints”. Anyone reading the code has to remember or guess what each position means.
This leads to misunderstandings, especially in larger or shared code bases.
Even if you write a comment like:
-- (day, month, year)
date :: (Int, Int, Int)
date = (5, 10, 2025)there’s nothing in the type enforcing:
- that the first element must be the day
- that the second must be the month
- that the third must be the year
A future programmer could accidentally swap month and day and the compiler would happily accept the code.
Because each field is simply an Int, all of the following are technically valid:
date1 :: (Int, Int, Int)
date1 = (-5, 10, 2025) -- negative day
date2 :: (Int, Int, Int)
date2 = (31, 31, 2025) -- 31st month (nonsense)
date3 :: (Int, Int, Int)
date3 = (400, 0, -9) -- all kinds of invalidThe compiler cannot tell that:
- 31 is not a valid month
- 0 is not a valid month
- negative days or years don’t make sense
Tuples don’t give you a place to enforce domain rules.
Type signatures like:
calculateSomething :: (Int, Int, Int) -> Booltell you nothing about:
- what that triple represents
- whether it is a position, a date, a color, or something else
If the same triple is meant to be a date, the type does not communicate that intent.
All these problems motivate defining custom types instead of relying on plain tuples.
Later lessons will show how you can do this using:
- Type synonyms (to improve readability)
-
newtype(to create distinct, type-safe wrappers) -
datadeclarations (to define rich algebraic data types)
For example, a more expressive type could look like:
data Date = Date Day Month Yearor:
data Date = Date
{ day :: Int
, month :: Int
, year :: Int
}With such definitions:
- The type name
Dateclearly communicates meaning - You can later add validation logic to ensure only valid dates are constructed
- You avoid accidentally swapping fields
Tuples are great for quick, small groupings, but as soon as the structure has domain meaning (like a date), using a custom type usually gives you:
- Better readability
- Fewer bugs
- Stronger guarantees from the type system
-
Cartesian product The set of all ordered pairs (or tuples) formed by combining elements of two sets.
-
Product type A type built by combining other types so that its possible values are the Cartesian product of the component types.
-
Tuple A built-in, ordered, fixed-size product type in Haskell, written with parentheses and commas, such as
(Int, Bool)or(String, Int, Int). -
Arity The number of components in a tuple or the number of arguments a function takes.
-
Type signature A declaration that specifies the type of an expression, variable, or function, e.g.
x :: Intorf :: Int -> Bool. -
Domain modeling The practice of designing types to reflect the real-world concepts and constraints of the problem you’re solving.
-
Invalid state A combination of values that is technically allowed by the type system but makes no sense in the real world (like a month value of 31).
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: