Bogoware.Monads brings two of functional programming’s plainest tools into idiomatic C#: a Maybe<T> for a value that may be absent, and a Result<T> for an operation that may fail. The point isn’t to make C# look like Haskell. It’s to move two whole families of bug out of the runtime and into the type system. A value that might not exist is a Maybe<T>, so the empty case can’t be forgotten. An operation that might fail returns a Result<T>, so the failure is a value you have to deal with, not an exception unwinding the stack from somewhere you weren’t looking.
Same bias as in the articles: make the awkward cases explicit and let the compiler hold you to them. Nulls and stray exceptions are the accidental kind of complication good design keeps out of the way; a Maybe or a Result in a signature is the model telling the truth about what can happen.
using Bogoware.Monads;
// Absence lives in the type — no nulls, no NullReferenceExceptions.
public string DisplayName(Maybe<string> nickname, string fallback) =>
nickname.Map(n => $"@{n}").GetValue(fallback);
// Failure is a value you must handle — no hidden exceptions.
public Result<User> Register(string email, string password) =>
ValidateEmail(email)
.Bind(() => ValidatePassword(password))
.Map(() => new User(email, password));
string message = Register(email, password).Match(
user => $"Welcome, {user.Email}",
error => $"Could not register: {error.Message}"); It composes the way you’d expect: Map, Bind, Match, LINQ query syntax, async overloads, and collection helpers for IEnumerable<Maybe<T>> and IEnumerable<Result<T>>. Errors are a small, extensible hierarchy rather than strings, so a failure still means something by the time it reaches the caller. Targets .NET Standard 2.1 and .NET 8/9/10.
- Package — nuget.org/packages/Bogoware.Monads
- Source — github.com/bogoware/monads
- Docs — bogoware.github.io/Monads