Table of Content
- Part 1 (this part)
- Functional Ideas
- Category theory
- LINQ, Lists and Optionals
- Part 2 (coming)
- Duality and RX Extensions
- Monads and async await
- Part 3 (coming)
- Abstract Data Types
- Part 4 (coming)
- Making Invalid States Unrepresentable

ou’ve Been Using Functional Ideas All Along—Now Understand Them
Many of the most important and widely adopted programming features of the last two decades have their roots in functional programming—and deeper still, in category theory. These ideas have spread across languages often underpinning popular features without developers realizing their origins. For object-oriented programmers, understanding these ideas can radically reshape how we think about types, structure, and behaviour in software.

The Value of Abstraction
Category theory gives us a mathematical language for thinking about types, how they relate, and how they can be transformed. Once you see this, you can't unsee it. Patterns across libraries and languages start to click. Abstractions become more coherent. Features you once struggled with begin to make sense—not because you've gotten smarter, but because you're seeing the bigger picture.
Often I see programmers struggle with new features, when there are older features that have similar properties and behaviour that they have no problem with. Understanding is only a slight shift in perspective away.
My First Steps: LINQ and Composability
My first brush with category theory came with C# 3.0 and the introduction of LINQ. At the time, I was refactoring a codebase and trying to make libraries more reusable. Lists and "optionals" were giving me grief. Iteration, filtering, paging, and threading all felt like they required too much boilerplate. My object-oriented toolset—design patterns, interfaces, inheritance hierarchies—wasn’t helping.
Then came LINQ, and suddenly, things started to fall into place.
Lists, Optionals, and What They Really Are
Beyond the Collection: Functors, Monads, and the Real Shape of Data
In category theory, structures like List<T>
or IEnumerable<T>
aren’t just containers—they’re functors and often monads. These might
sound like intimidating terms, but they capture consistent, powerful ways to model and transform data across
a wide range of scenarios.
Imperative programmers, especially in C#, often get caught up in choosing
between T[]
, List<T>
, Dictionary<TKey, TValue>
, or HashSet<T>
. While these distinctions matter for performance and semantics,
it’s more valuable to start with a higher-level question: what is the
shape of this data, conceptually? Is it a sequence, a choice, a mapping, a possible absence? This
shift in mindset—from implementation to abstraction—pays dividends in clarity and reuse.
The Optional Mess in C#
Calling nullable value types and reference types "optionals" is being generous. The functionality fell far short of what you would expect from a Maybe or Optional type.Nullable value types (
int?
, DateTime?
,
etc.) carried the right idea—a value that might be absent—but the
ergonomics were awkward. The HasValue
/Value
pattern was clunky, and the risk of misuse high. Reference types were worse:
everything was nullable, with no way in the type system to express a
non-null guarantee. That made every object a potential landmine.
The new extension methods worked on null reference types, however extension
methods and null checks helped only slightly. You could filter out null
values or use fluent chains to avoid some errors—but this was a teaspoon
against the ocean. A better approach was the Null
Object Software Design Pattern, replacing null
with benign default objects. It worked, but was too burdensome to apply
everywhere.
These weren’t uniquely C# issues. They’re legacy decisions, dating back to C, which
inherited them from older languages like ALGOL W. Tony Hoare, inventor of the null
reference, later called it his “billion
dollar mistake.”—an error so deep that it rippled across decades of software development.
Why This Was a Big Deal
The core issue was this: you were
asking humans to track where null
might sneak in.
And humans are terrible at boring, repetitive, unforgiving tasks. Worse, the static type system—the thing
that’s supposed to catch errors before they escape into production—was blind to it. NullReferenceException
became one of the most common runtime failures in C#,
and developers had no way to formally express or enforce "this value should never be null."

LINQ: Category Theory in Disguise
If you define two extension methods—Select
and SelectMany
—with
the right signatures, you can make almost anything LINQ-able. That’s the power of monads.
-
Select
(a.k.a.map
in FP circles) is for transforming values inside a context. Think: turning aList<int>
into aList<string>
, or aNullable<string>
into aNullable<bool>
. -
SelectMany
(a.k.a.flatMap
,bind
, or>>=
) flattens nested contexts: aList<List<T>>
becomes aList<T>
, aNullable<Nullable<T>>
becomes aNullable<T>
. This same idea powersasync/await
and promise chaining in JavaScript. Something similar is happening when a do try catch block intercepts an error from several layers down in the stack (This flattening behaviour is more obvious with languages with checked exceptions like Java or Swift)
?.
)—a syntactic sugar for optional objects that provides a
limited form of mapping for honest properties and binding for optional properties. On the upside it works
for both Nullables and reference types.?.
) was so popular it made it into
many other languages under an assortment of names.Functional Programming's Influence Grows
Around the same time, other functional staples were entering the
mainstream. Hadoop brought map
, reduce
, and filter
into the
spotlight. C# had higher-order functions from the start, but lambdas in C# 3.0 finally made them usable
without drowning in boilerplate.
LINQ’s use of extension methods also meant you could tack on new functionality to types you didn’t own—a clever trick that helped functional patterns sneak into OOP-heavy ecosystems. Without a coat of paint functional concepts are often resisted as being too procedural and not O-O enough.
Even the type system itself was changing. Parameterized types (aka generics) have roots in abstract algebra and were first seen in functional languages like ML (1973), then Ada (1977), then C++ templates (1991). Category theory takes this further with higher-kinded types—types that take types that take types.
Languages like Haskell and Scala can express these abstractions natively. C# can’t, but you can still use the ideas—just in a less elegant way.
The Big Realization
The real epiphany for me came when I saw that monadic operations acted on
containers—like List<T>
and Nullable<T>
—as whole
things. They respected encapsulation. I didn’t need to peek inside and mess with internals. I
could compose operations, transform values, and chain computations without writing glue code every time.
Contrast that with traditional imperative code, which often tears through internal structures and violates encapsulation to “get things done.” Functional concepts gave me a cleaner, more composable way to think.
Conclusion
You don’t need to become a Haskell expert or abandon object-oriented programming to benefit from category theory. It might feel foreign at first. But it will:
- Change how you think about types and data
- Help you recognize deeper patterns across languages and libraries
- Reduce boilerplate and improve composability
- Let you write more robust and expressive code
Gaining familiarity with these concepts can dramatically improve your ability to design clean, reusable, and maintainable code—especially as languages like C# continue to adopt features born in functional programming.
If you're serious about mastering modern software development, understanding the abstractions behind LINQ, monads, and functors is no longer optional—it's essential.
Related Posts
- Complexity and Intent vs Implementation
- Persisting Data, Code and WorkFlow Tracking
- UX & DX
- Making Work Actually Flow
- Managing Images and Sounds.html
My Learning Tools
My Anki flash cards
My Self Directed Learning Tracking Boards
More Information
Videos
2007 Era
- Don't fear the Monads by Brian Beckman
- Legendary Microsoft talk that demystifies monads for everyday developers. Shows how LINQ and async are rooted in the same abstractions.
- LINQ, C# Futures and Intellisense by Luke Hoban
2009 Era
- Tony Hoare — Null References: The Billion Dollar Mistake
- Fundamentalist Functional Programming by Erik Miejer
Modern Era
- Category Theory for Programmers by Milewsk
- Category Theory by Awodey
- The Catsters
- Category Theory for Beginners by Richard Southwell
Books
Sharpen your understanding of abstraction vs. implementation, to gain a deeper grasp of type systems and functional programming, and to acquire powerful mental models for software design.
C#

No comments:
Post a Comment