Skip to content

0020: Email Tutorials Haskell For Beginners – Product Types and Tuples in Haskell

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

LigerLearn Video 20

📑 Table of Contents

  • 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

20.1. Cartesian Products in Mathematics

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.

20.2. Product Types in Haskell

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:

  • x is a pair of Int values
  • Its possible values are all combinations (a, b) where a and b are valid Ints

Since Int has a fixed range, the total number of possible (Int, Int) values is the product of:

  • the number of possible Int values for the first component
  • times the number of possible Int values 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.

20.3. Tuples as Built-In Product 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:

  • user is a pair of a String and an Int
  • 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.

20.4. Modeling Data with Tuples: The Date Example

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.

20.5. Limitations of Tuples for Real-World Data

While tuples work mechanically, they are not a great modeling tool for non-trivial data. The main problems are:

1. No Meaning in the Type

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.

2. No Position Semantics

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.

3. No Validation or Constraints

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 invalid

The 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.

4. Poor Communication in Type Signatures

Type signatures like:

calculateSomething :: (Int, Int, Int) -> Bool

tell 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.

20.6. Why Custom Types Are Better

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)
  • data declarations (to define rich algebraic data types)

For example, a more expressive type could look like:

data Date = Date Day Month Year

or:

data Date = Date
  { day   :: Int
  , month :: Int
  , year  :: Int
  }

With such definitions:

  • The type name Date clearly 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

20.7. Glossary of Terms

  • 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 :: Int or f :: 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).

🎓 Quiz

Quizz & Progress Badge NFT

Clone this wiki locally