MonadicSharp 1.4.0

dotnet add package MonadicSharp --version 1.4.0
                    
NuGet\Install-Package MonadicSharp -Version 1.4.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="MonadicSharp" Version="1.4.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="MonadicSharp" Version="1.4.0" />
                    
Directory.Packages.props
<PackageReference Include="MonadicSharp" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add MonadicSharp --version 1.4.0
                    
#r "nuget: MonadicSharp, 1.4.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package MonadicSharp@1.4.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=MonadicSharp&version=1.4.0
                    
Install as a Cake Addin
#tool nuget:?package=MonadicSharp&version=1.4.0
                    
Install as a Cake Tool

MonadicSharp

NuGet Version NuGet Downloads CI License: MIT .NET

Railway-Oriented Programming for C#. Replace exception-driven control flow with composable, explicit error handling.

dotnet add package MonadicSharp

The problem

// You never know what this can throw, or when, or why
public User CreateUser(string name, string email)
{
    if (string.IsNullOrWhiteSpace(name))
        throw new ValidationException("Name is required");

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

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

    return _db.Save(new User(name, email));
}

Every caller needs a try/catch. Errors are invisible in the signature. Composing multiple operations that can fail is painful.

The solution

public Result<User> CreateUser(string name, string email)
{
    return 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 exceptions, no hidden branches.


Core types

Result<T> — success or failure

Result<int> Parse(string input) =>
    int.TryParse(input, out var n)
        ? Result<int>.Success(n)
        : Result<int>.Failure(Error.Validation("Not a number", field: "input"));

var result = Parse("42")
    .Map(n => n * 2)
    .Where(n => n < 200, "Value too large")
    .Match(
        onSuccess: n  => $"Result: {n}",
        onFailure: err => $"Error: {err.Message}"
    );
// "Result: 84"

Option<T> — value or nothing

Option<User> FindUser(int id) =>
    _db.TryGetValue(id, out var user) ? Option<User>.Some(user) : Option<User>.None;

string name = FindUser(42)
    .Map(u => u.Name.ToUpper())
    .GetValueOrDefault("Anonymous");

Error — structured, composable errors

// Semantic factory methods
Error.Validation("Name is required", field: "name")
Error.NotFound("User", identifier: id.ToString())
Error.Forbidden("Admin access required")
Error.Conflict("Email already registered", resource: "email")
Error.FromException(ex)

// Enrich with context
Error.Create("Payment failed")
    .WithMetadata("orderId", order.Id)
    .WithMetadata("amount", order.Total)
    .WithInnerError(gatewayError)

// Combine multiple errors
Error.Combine(nameError, emailError, ageError)

Either<TLeft, TRight> — two explicit tracks

Either<Error, User> Authenticate(string token) =>
    _tokenService.Validate(token)
        ? Either<Error, User>.FromRight(_users.GetByToken(token))
        : Either<Error, User>.FromLeft(Error.Forbidden("Invalid token"));

var message = Authenticate(token).Match(
    onLeft:  err  => $"Denied: {err.Message}",
    onRight: user => $"Welcome, {user.Name}"
);

Async pipelines

Chain async operations with automatic short-circuiting on failure:

var result = await GetOrderAsync(orderId)        // Task<Result<Order>>
    .Then(ValidateInventoryAsync)                // stops here if invalid
    .Then(ReserveStockAsync)
    .Then(SendConfirmationEmailAsync)
    .ExecuteAsync();

Conditional steps

await ProcessOrder(order)
    .ThenIf(o => o.Total > 1000, ApplyDiscountAsync)
    .ThenIf(o => o.IsInternational, CalculateShippingAsync)
    .ExecuteAsync();

Built-in retry

await GetOrder(id)
    .ThenWithRetry(
        operation:   CallExternalPaymentApiAsync,
        maxAttempts: 3,
        delay:       TimeSpan.FromSeconds(2))
    .ExecuteAsync();

Entity Framework Core integration

DbSetExtensions wraps EF Core operations to return Result<T> and Option<T> instead of throwing:

// FindAsync returns Option<T> — no null checks needed
Option<User> user = await _db.Users.FindAsync(id);

// AddAsync returns Result<T>
Result<User> created = await _db.Users.AddAsync(newUser);

// Composable pipeline with EF
var result = await _db.Users.FindAsync(id)
    .ToResult(Error.NotFound("User", id.ToString()))
    .BindAsync(user => _db.Users.Update(updatedUser))
    .BindAsync(_ => _db.SaveChangesAsync());

Collecting multiple errors

Sequence and Traverse let you run all validations and collect every failure — not just the first one:

var errors = new[]
{
    ValidateName(name),
    ValidateEmail(email),
    ValidateAge(age)
}.Sequence();

errors.Match(
    onSuccess: values => Save(values),
    onFailure: err    => ReturnAllErrors(err.GetAllErrors())
);

Project templates

Scaffold a full project pre-wired with MonadicSharp:

dotnet new install MonadicSharp.Templates

dotnet new monadic-api   -n MyApi    # Minimal API + Result pattern + EF Core
dotnet new monadic-clean -n MyApp    # Clean Architecture + CQRS + MediatR

Requirements

  • .NET 8.0+
  • C# 10.0+

Contributing

Issues and pull requests are welcome. See CHANGELOG for version history.

License

MIT — see LICENSE.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (9)

Showing the top 5 NuGet packages that depend on MonadicSharp:

Package Downloads
MonadicSharp.AutoMapper

Lightweight, functional-friendly object mapper for MonadicSharp. Provides mapping configuration and extensions for Option, Result, and Either.

MonadicSharp.DI

MonadicSharp.DI - lightweight private mediator (in-process) aligned with MonadicSharp functional primitives (Result, Option, etc.).

MonadicSharp.AI

AI-specific extensions for MonadicSharp: typed error handling for LLM APIs, exponential backoff retry, agent execution tracing, structured output validation, and streaming completion support. The functional reliability layer for enterprise .NET AI systems.

MonadicSharp.Http

Result-aware HTTP client for MonadicSharp — HTTP errors, timeouts, and deserialization failures are first-class Result values. Includes typed retry policies with exponential backoff and integration with MonadicSharp.Agents CircuitBreaker.

MonadicSharp.Agents

Typed multi-agent orchestration for .NET — capability-based sandboxing, railway-oriented pipelines, circuit breakers, and immutable audit trails. Build secure, composable AI agent systems with compile-time guarantees.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.4.0 110 2/24/2026
1.3.5 233 10/8/2025
1.3.4 184 10/8/2025
1.3.3 190 6/26/2025
1.3.0 365 6/12/2025
1.2.0 339 6/10/2025
1.1.0 334 6/9/2025
1.0.0 166 6/7/2025

v1.4.0: Added comprehensive xUnit test suite, GitHub Actions CI/CD, CHANGELOG, usage examples, and fixed package dependencies
v1.3.0: Complete rebranding from FunctionalSharp to MonadicSharp - Templates, documentation, and package naming fully unified
v1.2.0: Changed namespace from FunctionalSharp to MonadicSharp to match NuGet package name
v1.1.0: Added Either<TLeft, TRight> type for representing two alternative types