Coproducts — Modeling “Either This or That” in Code
Most bugs don’t come from what your program does. They come from what your program **assumes cannot happen**.
You think a value will always be present. You assume a request will always succeed. You believe a state will always be valid.
Reality disagrees. A user can be unauthenticated. An API can fail. A screen can be loading.
So the real question is:
That’s where Coproducts come in.
The Core Idea
A coproduct represents a choice:
A + BMeaning:
A value is either **A OR B** Programming Intuition
In Kotlin:
sealed class Result<out T> {
data class Success<T>(val value: T) : Result<T>()
data class Error(val message: String) : Result<Nothing>()
}This means:
Result = Success + ErrorAt runtime, it’s exactly one of them
Why Not Just Use Null?
val user: User? = nullThis tells you that a value is missing, but it does not explain the reason behind it.
Coproducts fix this:
Result.Error("User not found")Now your system knows what happened
Category Theory View
A coproduct of A and B is an object C with two injections:
i : A → C
j : B → CMeaning
The Universal Property (Important Insight)
If you know how to handle:`
A → X
B → XThen you automatically know how to handle:
(A + B) → X
Kotlin Equivalent (Factorizer)
fun <A, B, C> fold(
left: (A) -> C,
right: (B) -> C
): (Result<A,B>) -> C = { result ->
when (result) {
is Result.Success -> left(result.value)
is Result.Error -> right(result.error)
}
}
sealed class Result<out A,out B> {
data class Success<A>(val value: A) : Result<A, Nothing>()
data class Error<B>(val error: B) : Result<Nothing,B>()
}This is the coproduct factorizer
Real-World Use Case (Android)
UI State
When building apps, we often represent UI like this:
sealed class UiState {
object Loading : UiState()
data class Success(val data: String) : UiState()
data class Error(val message: String) : UiState()
}Why this matters
A common (but problematic) way to model UI state is:
var loading = false
var data: String? = null
var error: String? = nullAt first glance, this looks simple.
But it introduces a serious issue:
• loading = true and data != null can exist together
• error != null and data != null can exist together
These are invalid combinations, but nothing prevents them.
What coproduct gives us
With: UiState
You are saying:
At any moment, the UI is in exactly one state.
• Loading
• Success
• Error
No invalid combinations are possible.
This is the essence of a coproduct:
A value can be one of many possibilities, but only one at a time.
Composition (Short-Circuiting)
Coproducts are not just about modeling — they also improve how we write logic.
Consider this pipeline:
validate(id) .flatMap(::fetchUser) .flatMap(::mapUi)
What happens here?
• If validate fails → everything stops
• If fetchUser fails → next step is skipped
• If all succeed → final result is produced
You don’t write manual checks like:
if (error != null) return errorThe structure itself ensures:
Once an error happens, the rest of the pipeline does not run.
Why this is powerful
• No nested if conditions
• No scattered error handling
• Clear flow of data
The system enforces correctness for you.
Final takeaway
• Product → combine values
• Coproduct → choose one possibility
Coproducts let you model real-world situations where something can be in one state at a time — and they ensure your code stays correct by design.
