Skip to content
Bogoware logo

Bogoware.Monads

Bring the elegance of functional error handling to your C# codebase

Composable Result<T> and Maybe<T> monads that replace exceptions with explicit, chainable pipelines — so your code reads like intent, not accident.

NuGetDownloads.NET versionsChangelog

Why?

Exceptions are invisible control flow. A method signature that returns User might throw five different exceptions — but the compiler won’t tell you, and the caller won’t know until production. You’re left guessing which try/catch blocks to write, wrapping everything defensively, and hoping nothing slips through.

Monads make failure explicit. A method that returns Result<User> declares — right in its signature — that it can fail. The compiler enforces handling. Chaining operations with .Bind() and .Map() creates a clean pipeline where errors short-circuit automatically. No hidden jumps, no forgotten catch blocks, no surprises.

This is railway-oriented programming: your code flows along a success track until something fails, then it switches to the failure track. Every step is visible, composable, and testable. It’s an idea from functional programming — applied to real-world C#, with full async support and zero ceremony.

Key Features

Result Monad

Model operations that can fail with explicit, composable error handling — no more hidden exceptions.

Maybe Monad

Handle optional values safely with chainable operations — a superior alternative to null checks.

Railway-Oriented Programming

Chain operations fluently along success and failure tracks for clean, readable pipelines.

Typed Error Hierarchy

Distinguish between logic errors and runtime errors with a structured, extensible error model.

Collection Extensions

Work with sequences of monadic values using MapEach, BindEach, MatchEach, and AggregateResults.

Full Async Support

Every operation has async overloads that work seamlessly with Task-based patterns.

Quick Start

  1. Install from NuGet

    Terminal window
    dotnet add package Bogoware.Monads
  2. Handle optional values with Maybe

    using Bogoware.Monads;
    public record Person(string FirstName, Maybe<string> LastName);
    var person = new Person("Jane", Maybe.None<string>());
    string greeting = person.LastName
    .Map(last => $"Hello, {person.FirstName} {last}!")
    .GetValue($"Hello, {person.FirstName}!");
    // → "Hello, Jane!"
  3. Build explicit error pipelines with Result

    public Result<User> CreateUser(string email, string password)
    => ValidateEmail(email)
    .Bind(_ => ValidatePassword(password))
    .Map(_ => new User(email, password));
    // Errors short-circuit — no try/catch needed
    var result = CreateUser("bad-email", "short");
    result.Match(
    user => $"Created: {user.Email}",
    error => $"Failed: {error.Message}"
    );

Library Overview

TypePurposeKey Operations
Result<T>Operations that can failMap, Bind, Match, Ensure, RecoverWith
Maybe<T>Optional values without nullMap, Bind, Match, GetValue, MapToResult
ErrorStructured error hierarchyLogicError, RuntimeError, AggregateError
UnitVoid-equivalent for monadic returnsUsed with Result<Unit> for side-effect operations

Next Steps