Result Monad
Model operations that can fail with explicit, composable error handling — no more hidden exceptions.
Composable Result<T> and Maybe<T> monads that replace exceptions with explicit, chainable pipelines — so your code reads like intent, not accident.
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.
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.
Install from NuGet
dotnet add package Bogoware.MonadsHandle 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!"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 neededvar result = CreateUser("bad-email", "short");result.Match( user => $"Created: {user.Email}", error => $"Failed: {error.Message}");| Type | Purpose | Key Operations |
|---|---|---|
Result<T> | Operations that can fail | Map, Bind, Match, Ensure, RecoverWith |
Maybe<T> | Optional values without null | Map, Bind, Match, GetValue, MapToResult |
Error | Structured error hierarchy | LogicError, RuntimeError, AggregateError |
Unit | Void-equivalent for monadic returns | Used with Result<Unit> for side-effect operations |