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.Telemetry

Observability layer for MonadicSharp.Agents — OpenTelemetry-compatible metrics and distributed tracing for agent executions, pipelines, and circuit breakers. Zero external dependencies: built on System.Diagnostics.Metrics and System.Diagnostics.ActivitySource.

MonadicSharp.Caching

Result-aware caching layer for MonadicSharp — cache misses and errors are first-class Result values, never null or exceptions. Supports IMemoryCache, IDistributedCache, and transparent agent output caching via CachingAgentWrapper.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.4.0 109 2/24/2026
1.3.5 233 10/8/2025
1.3.4 182 10/8/2025
1.3.3 189 6/26/2025
1.3.0 364 6/12/2025
1.2.0 339 6/10/2025
1.1.0 333 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