Skip to content

MonadicSharpRailway-Oriented Programming

Replace exception-driven control flow with composable, explicit error handling — zero dependencies, pure .NET.

MonadicSharp
NuGet VersionNuGet DownloadsCIMIT License.NET 8

Before vs After

The same operation — one with exceptions, one with Railway-Oriented Programming:

csharp
// Exception-driven: contract invisible to callers
public User CreateUser(string name, string email)
{
    if (string.IsNullOrWhiteSpace(name))
        throw new ValidationException("Name required");

    if (!email.Contains('@'))
        throw new ValidationException("Invalid email");

    var existing = _db.Users
        .FirstOrDefault(u => u.Email == email);
    if (existing != null)
        throw new ConflictException("Email taken");

    return _db.Save(new User(name, email));
}
// Every caller needs try/catch.
// Errors are invisible in the signature.
csharp
// Railway-Oriented: contract explicit in the return type
public Result<User> CreateUser(string name, string email) =>
    ValidateName(name)
        .Bind(_ => ValidateEmail(email))
        .Bind(_ => CheckEmailNotTaken(email))
        .Map(_ => new User(name, email))
        .Bind(user => _db.Users.AddAsync(user));

// The signature tells the truth.
// Errors propagate automatically — no try/catch.
// Compose at the controller with Match:
return result.Match(
    onSuccess: user  => Ok(user),
    onFailure: error => error.Type switch {
        ErrorType.Validation => BadRequest(error.Message),
        ErrorType.Conflict   => Conflict(error.Message),
        _                    => Problem(error.Message)
    });

Ecosystem

MonadicSharp is a family of focused packages — use only what you need.

Ecosystem · Templates · Try it ▶

Released under the MIT License.