Arun Pandian M

Arun Pandian M

Android Dev | Full-Stack & AI Learner

Asymmetry — Why Products and Coproducts Feel So Different

Category theory often promises symmetry. But real systems remind us: **direction changes everything**.

We saw something beautiful:

  • Initial ↔ Terminal (reverse arrows)
  • Product ↔ Coproduct (reverse arrows)
  • Mathematically, they are dual.

    But then something surprising happens:

    In the category of sets… they behave *very differently*.

    Why?

    Because functions are asymmetric.

    Duality vs Reality

    Category theory gives us a beautiful and powerful idea:

    Reverse arrows → get a dual concept

    That’s why we get pairs like:

  • Product ↔ Coproduct
  • Initial ↔ Terminal
  • From a theoretical point of view, these are perfect duals — one is obtained from the other just by reversing the direction of arrows.

    But what happens in the real world (Set)?

    When we move from abstract theory to actual sets, something interesting appears:

    |A × B| = |A| × |B|  
    |A + B| = |A| + |B|

    Now the difference becomes concrete:

  • Product behaves like multiplication
  • Coproduct behaves like addition
  • Why this happens

    A product combines values:

  • you take one value from A and one value from B and form a pair
  • You get all possible combinations

    A coproduct represents a choice:

    you take a value from A or a value from B but never both

    You only get one side at a time

    Even though product and coproduct are dual in theory:

    They represent fundamentally different ideas in practice.
  • Product → AND (both exist)
  • Coproduct → OR (one exists)
  • Where duality “breaks”

    Duality is about structure (arrows), but real behavior comes from values and functions.

    Since functions:

  • can lose information
  • are not always reversible
  • reversing arrows does not preserve behavior.
    Duality gives us symmetry in theory, but in reality, combining and choosing are fundamentally different operations.

    Initial vs Terminal — Where Asymmetry First Appears

    One of the earliest places where symmetry breaks is in the behavior of initial and terminal objects.

    In theory, they are duals. In reality, they behave very differently.

    Initial Object (Empty Set ∅)

    ∅ → A  (unique)

    There is exactly one function from the empty set to any set:

    absurd : ∅ → A

    Why?

    Because there are no elements in ∅ — nothing needs to be mapped.
    So the function exists trivially.

    But what about the reverse?

    A → ∅  // (almost never exists)

    This is impossible in general. A function must map every element of A, but ∅ has no elements to receive them.

    No elements → nothing to map back

    Terminal Object (Singleton 1)

    A → 1  (unique)

    There is exactly one function from any set to the singleton.

    Why?

    Because all values collapse into the single element of 1.

    But now, the reverse behaves very differently:

    1 → A (many functions)

    Each function corresponds to choosing one element from A.

    Every function “picks” a value from A

    Even though initial and terminal objects are dual in theory:

  • ∅ can only send (but not receive)
  • 1 can both receive and send, but in very different ways
  • ObjectIncoming ar rowsOutgoing arrows
    ∅ (Initial)noneunique
    1 (Terminal)uniquemany
    https://storage.googleapis.com/lambdabricks-cd393.firebasestorage.app/fp_assymetry.svg?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=firebase-adminsdk-fbsvc%40lambdabricks-cd393.iam.gserviceaccount.com%2F20260405%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20260405T154714Z&X-Goog-Expires=3600&X-Goog-SignedHeaders=host&X-Goog-Signature=a79338d03002ec81368c96d07f980964616a9e1f6e19b8f9b209f9e9044051951e70b4696776be6ea1fb3a68dbf04bac1172e93a664b12e9241c2e684e184b02d48474aeade434a4e38746ae9096b4800a7d2b9bd9fc8ee23c0d5577163725bd5b1dc5ebb8ec18b7e040a42c3103a14780f3306a0d03fada8cd354e8b379d8d190fd69f0974f12c66c21aeaa5ceb9b3d1d90b99f7e359b63a4b16f6ad79d77c654858f0ca79b46a9c228fea2816b5666e0362d98995c72ddca9835c9f7078beaf98134817e23651daf187b9a15499526a4d8429ec909f2093d888c6a97c3d3ccb99e2a73091c2014e129dd3436658077b25c1404250d55ab8451462f1f82d45b

    Types of Functions — Where Symmetry Survives (and Where It Breaks)

    To truly understand asymmetry, we need to look at how functions behave.

    Not all functions are the same. Some preserve information, some lose it, and only a few are perfectly reversible.

    Injective (One-to-One)

    a₁ ≠ a₂ ⇒ f(a₁) ≠ f(a₂)

    Different inputs always produce different outputs.

    No two values from A collide in B

    Intuition:

  • Information is not lost
  • You can distinguish inputs by looking at outputs
  • Example:

    fun double(x: Int): Int = x * 2

    • 1 → 2

    • 2 → 4

    No two inputs give the same output.

    But…

    Even though nothing is lost:

    You may not be able to go back fully (because some values in B may not come from A)

    Surjective (Onto)

    ∀ b ∈ B, ∃ a ∈ A such that f(a) = b

    Every value in B is covered by the function.

    Nothing in B is left unused

    Intuition:

    • Output space is fully covered

    • But multiple inputs may map to the same output

    Example:

    fun parity(x: Int): Boolean = x % 2 == 0

    • Many inputs → same output (true or false)

    Information is lost

    You cannot tell which input produced the result.

    Bijective (Perfect Symmetry)

    A ≅ B

    A function is bijective if it is:

    • Injective (no collisions)

    •Surjective (covers everything)

    What it means

    Every input maps to a unique output
    Every output comes from exactly one input

    Intuition:

    • No information is lost

    • No ambiguity exists

    • Fully reversible

    Example:

    fun encode(x: Int): String = x.toString()
    fun decode(s: String): Int = s.toInt()

    You can go forward and backward safely.

    Why only bijections are truly symmetric

    Only bijections allow:

    A ⇄ B
    Forward and backward behave the same

    Final understanding

    TypeInformation LossReversible
    InjectiveNoNot always
    SurjectiveYesNo
    BijectiveNoYes
    A function is symmetric only when nothing is lost and nothing is duplicated — that’s exactly what a bijection guarantees.

    This is the core reason why:

    Most real-world systems are asymmetric — because most functions are not bijections.

    Programming View — How Asymmetry Shows Up in Real Code

    So far, we’ve explored asymmetry in theory. Now let’s see how it naturally appears in everyday programming.

    At the core, everything comes down to one idea:

    Functions either preserve information or lose information

    And this is what creates asymmetry.

    Embedding (small → big)

    val f: (Unit) -> Int = { 42 }

    • Unit has only one possible value

    • Int has many possible values

    So this function doesn’t “compute” anything from input.

    It simply chooses one value from a larger space

    Important clarification

    Even though this function always returns 42:

    • The type Int contains many possible values

    • This function picks one of them

    Other possible functions could return 1, 100, etc.
    Embedding places a value into a richer space without losing structure

    Collapsing (many → one)

    val g: (String) -> Unit = { Unit }

    • String has many possible values

    • Unit has only one

    So:

    All inputs map to the same output

    Result

    • Information is lost

    • Different inputs become indistinguishable

    Collapsing destroys structure by removing distinctions

    Composition — where asymmetry grows

    collapse ∘ collapse = more collapse

    Each transformation may lose some information.

    When combined: The loss accumulates

    val result =
        user
            .name        // lose structure
            .length > 5  // lose even more

    Boolean // Final result

    • You started with a rich object (User)

    • You ended with a simple value (Boolean)

    You cannot go back

    Summary

  • Embedding → preserves structure
  • Collapsing → destroys structure
  • Composition → amplifies information loss
  • Even though category theory gives us dual definitions, the real world (functions) breaks symmetry.
    #TypeTheory#AlgebraicDataTypes#BuildInPublic#SoftwareDesign#InformationFlow#FPConcepts#CategoryTheory#DataModeling#FunctionalProgramming#Asymmetry#KotlinFP#ProductType#MathForDevelopers#Coproduct#LearnInPublic