Introduction

What are Monads?

A monad is a design pattern from functional programming. Think of it as a smart wrapper around a value that lets you chain operations without checking for errors at every step. The wrapper carries the result or the failure through the pipeline — you only handle the outcome at the end.

Classic examples: Option<T> (maybe a value), Result<T, E> (success or error). ApiResult<T> is a result monad tailored for HTTP calls.


C# 15 Union Types

C# 15 introduces union types (discriminated unions): a type that holds exactly one of several defined cases.

public readonly union ApiResult<T>(
    Success<T> success,
    HttpError httpError,
    TransportError transportLevelError
);

This replaces inheritance hierarchies or OneOf<> libraries with first-class language support. The compiler knows all possible cases, so pattern matching is exhaustive and clean.

Union types are the key enabler — they let us do functional-style result chaining in idiomatic C#.


The Three Cases

Case Meaning
Success<T> HTTP 2xx + valid deserialized body
HttpError HTTP 4xx/5xx response
TransportError Socket/network/timeout exceptions

Every ApiResult<T> is exactly one of these three — no nulls, no exceptions leaking out.