Arun Pandian M

Arun Pandian M

Android Dev | Full-Stack & AI Learner

Written by: Arun Pandian M•Published on: Apr 3, 2026

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 + B

Meaning:

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 + Error

At runtime, it’s exactly one of them

Why Not Just Use Null?

val user: User? = null

This 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 → C

Meaning

  • You can put A into C
  • You can put B into C
  • The Universal Property (Important Insight)

    If you know how to handle:`

    A → X  
    B → X

    Then 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

    https://storage.googleapis.com/lambdabricks-cd393.firebasestorage.app/coproduct_factorizer.svg?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=firebase-adminsdk-fbsvc%40lambdabricks-cd393.iam.gserviceaccount.com%2F20261003%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20261003T062417Z&X-Goog-Expires=3600&X-Goog-SignedHeaders=host&X-Goog-Signature=50982fdcd15392d6dfb5a665380e1c06902a94d76f5d87e2b1ecae227e86fe4f04c216eda151a8d08f59b96701d4c107051653861350619068f2f46f93cecca7308c27184d4a0ee6c88b517df845f28a1cb5fbd861e8c55592b541b10b36066d7af47757eba95be1e8e8d55fea4afb9e5d1d5dd574edc469bf299e8b06e3e7b48e19fba484d4ed2df19c5b76ccef4ab7fb1087fb7d810d05a4fac22a4a3ca16500e69350b54930d2309e1dea6b4afdc285a3c9afcaa0d1012f108ad21c7f3207004bdc5f808b330903ca67275dba40a130c270333ed4e4d9b844cbf819608e1f4be58f4b25270e8e750054ecc53f6dd51c652418509b7eedb7b4795d6392ab5d

    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? = null

    At 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 error

    The 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.
    #typetheory#algebraicdatatypes#buildinpublic#composablesystems#softwaredesign#fpconcepts#eithertype#categorytheory#datamodeling#functionalprogramming#errorhandling#kotlinfp#mathfordevelopers#coproduct#learninpublic
    LAMBDA BRICKS