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
- LINQ, C# Futures and Intellisense by Luke Hoban
Modern Era
- Category Theory for Programmers by Milewsk
- Category Theory by Awodey
- The Catsters
- Category Theory for Beginners by Richard Southwell