Product Types: Why Types Behave Like Multiplication
There’s a point in programming where types stop feeling like simple containers and start behaving like algebra.
At first, a pair or a data class looks ordinary:
data class User(
val name: String,
val age: Int
)But underneath, something deeper is happening.
This User type is not just a structure holding values. It is actually a product type — one of the foundational building blocks of Algebraic Data Types (ADTs), Functional Programming, and Category Theory.
Once you see this, you start noticing algebra everywhere in programming.
What Is a Product Type?
A product type is a type that combines multiple values together into a single structure.
In Kotlin:
data class User(
val name: String,
val age: Int
)contains:
together.
You can think of it mathematically as:
String × IntThe × is important.
Product types are called “products” because the number of possible values multiplies.
Why “Product”?
Suppose:
Bool = { true, false }and:
Color = { red, green, blue }Then:
Pair<Boolean, Color>can contain:
(true, red)
(true, green)
(true, blue)
(false, red)
(false, green)
(false, blue)There are:
2 × 3 = 6. possible combinations. That multiplication behavior is why these are called product types.
The Canonical Product Type: Pair
Most programming languages implement product types using:
In Kotlin:
val user =
Pair("Arun", 28)or:
val user =
"Arun" to 28This is simply:
String × IntProduct Types Combine Information
A product type means:
ALL values exist together.
For example:
data class Address(
val city: String,
val zipCode: String
)An Address always contains:
simultaneously.
This is very different from sum types like sealed class, where only one possibility exists at a time.
Product Types Are Not Strictly Commutative
Consider these two types:
Pair<Int, Boolean>and:
Pair<Boolean, Int>They are different types. You cannot pass one where the other is expected. The compiler treats them differently because the order matters. But something interesting is happening underneath.
Same Information, Different Arrangement
Even though:
Pair<Int, Boolean>and:
Pair<Boolean, Int>are structurally different, they still carry the same information:
This is similar to:
The arrangement changes. The information does not.
Commutativity Up To Isomorphism
There exists a reversible transformation between them.
fun <A, B> swap(
pair: Pair<A, B>
): Pair<B, A> {
return Pair(
pair.second,
pair.first
)
}Nothing is lost. We can always reverse the operation.
That means the two structures are: isomorphic
Mathematically:
(A, B) ≅ (B, A)This idea is called:
commutativity up to isomorphismThe structures are not strictly equal, but they are structurally equivalent.
Nested Product Types
We can combine more than two values.
One way is nesting pairs:
((A, B), C)or:
(A, (B, C))These look different. But again, they contain the same information.
Both contain:
The grouping changes. The information does not.
Associativity Up To Isomorphism
We can transform between the two structures:
fun <A, B, C> reassociate(
value: Pair<Pair<A, B>, C>
): Pair<A, Pair<B, C>> {
return Pair(
value.first.first,
Pair(
value.first.second,
value.second
)
)
}Again:
So:
((A,B),C)
≅
(A,(B,C))This mirrors arithmetic associativity:
(a × b) × c
=
a × (b × c)except here the equality is: up to isomorphism
not strict equality.
Representation vs Information
This is one of the deepest ideas in category theory.Different structures may represent the same information.
Category theory often cares more about:
structure preservationthan exact representation.
If two structures can be transformed back and forth without losing information, they are often treated as “essentially the same.”
Unit Type as Identity
Now things get even more algebraic.
In arithmetic:
a × 1 = abecause multiplying by 1 changes nothing. Product types have something similar. The Unit type behaves like 1.
In Kotlin: Unit has exactly one possible value.
So:
Pair<String, Unit>does not really contain more information than: String
The Unit contributes nothing meaningful.
Isomorphism With Unit
We can remove Unit:
fun <A> rho(
pair: Pair<A, Unit>
): A = pair.firstand add it back:
fun <A> rhoInv(
value: A
): Pair<A, Unit> =
Pair(value, Unit)So:
(A, Unit) ≅ A
Again:
Types Start Behaving Like Algebra
At this point, product types begin looking very mathematical.
| Algebra | Types |
|---|---|
| multiplication | product types |
| associativity | tuple regrouping |
| identity element | Unit |
| commutativity | swap isomorphism |
This is why they are called:
Algebraic Data TypesTypes behave algebraically.
Product Types in Kotlin
Kotlin uses product types everywhere.
Data Classes
data class User(
val name: String,
val age: Int
)UI State
data class UiState(
val loading: Boolean,
val users: List<User>,
val error: String?
)API Models
data class ApiResponse(
val code: Int,
val message: String
)Domain Modeling
data class Money(
val amount: Double,
val currency: String
)All of these are product types. They combine independent pieces of information into one structure.
Why Named Product Types Matter
Tuples become difficult to understand as systems grow.
This is bad:
Pair<String, Boolean>What does the Boolean mean?
Instead, named product types communicate meaning:
data class Statement(
val text: String,
val valid: Boolean
)Now the structure carries semantic meaning.
This improves:
Type Constructor vs Data Constructor
In Haskell:
data Pair a b = P a btwo different ideas exist.
Type Constructor
Paircreates TYPES.
Example:
Pair String BoolData Constructor
P creates VALUES.
Example:
In Haskell:
P "Hello" TrueKotlin hides this distinction because classes act as both.
Product Types as ADT Foundations
Product types are one half of Algebraic Data Types.
ADTs are built from:
Together, they form the foundation of functional programming data modeling.
Final Thoughts
Product types may look simple at first.
But underneath, they reveal something profound:
types preserve structure algebraicallyPairs, tuples, data classes, records, and structs are not just containers. They are mathematical compositions of information.
And once you begin seeing product types as algebra instead of syntax, a large part of functional programming and category theory starts making much more sense.
