A modern functional programming library for C# featuring Result<T>, Option<T>, Railway-Oriented Programming, and fluent pipeline composition. Build robust applications with elegant error handling and monadic patterns.
A professional-grade functional programming library for C# featuring Result, Option, Railway-Oriented Programming, and advanced monadic patterns. Build enterprise-ready applications with bulletproof error handling and sophisticated control flow.
MonadicSharp follows a clean, modular architecture:
MonadicSharp/
├── Core/ # Core monadic types
│ ├── Result.cs # Railway-oriented programming
│ ├── Option.cs # Null-safe operations
│ ├── Try.cs # Exception handling
│ ├── Either.cs # Dual-state modeling
│ ├── Ensure.cs # Contract validation
│ └── Unit.cs # Functional unit type
├── Extensions/ # Extension methods
│ ├── FunctionalExtensions.cs
│ ├── PipelineExtensions.cs
│ └── DbSetExtensions.cs
└── Documentation/ # Comprehensive docs
├── ATTRIBUTION.md
└── INTEGRITY.md
dotnet add package MonadicSharpusing MonadicSharp.Core;
using MonadicSharp.Extensions;
// Chain operations with automatic error propagation
var result = Result.Success(42)
.Map(x => x * 2)
.Bind(x => x > 50 ? Result.Success(x) : Result.Failure("Value too small"))
.Map(x => $"Final value: {x}");
result.Match(
onSuccess: value => Console.WriteLine(value),
onFailure: error => Console.WriteLine($"Error: {error}")
);var safeResult = GetUser()
.Map(u => u.Email)
.Filter(email => email.Contains("@"))
.GetValueOrDefault("no-email@example.com");var robustOperation = Try.Execute(() => RiskyOperation())
.ExecuteWithRetry(maxAttempts: 3, delayMs: 1000)
.ExecuteWithCircuitBreaker(failureThreshold: 5, timeoutMs: 30000)
.Match(
onSuccess: result => $"Success: {result}",
onFailure: ex => $"Failed after all attempts: {ex.Message}"
);Either<ValidationError, User> processUser = ValidateUser(userData)
.Match(
left: error => Either.Left<ValidationError, User>(error),
right: validData => Either.Right<ValidationError, User>(CreateUser(validData))
);var validatedUser = Ensure.That(user)
.IsNotNull("User cannot be null")
.Satisfies(u => u.Age >= 18, "User must be an adult")
.Satisfies(u => !string.IsNullOrEmpty(u.Email), "Email is required")
.Value; // Throws if any condition failsGet started quickly with MonadicSharp using our pre-built project templates:
# Install MonadicSharp templates
dotnet new install MonadicSharp.Templatesmonadic-api)A lightweight REST API template with functional programming patterns:
dotnet new monadic-api -n MyApi
cd MyApi
dotnet runmonadic-clean)A comprehensive Clean Architecture template with CQRS and functional patterns:
dotnet new monadic-clean -n MyApp
cd MyApp
dotnet runBoth templates include:
📖 Learn more: Check the Templates README for detailed documentation
The foundation of error handling without exceptions:
// Success and failure creation
var success = Result.Success("Hello World");
var failure = Result.Failure<string>("Something went wrong");
// Advanced chaining with validation
var result = GetUser(id)
.Bind(ValidateUser)
.Bind(EnrichUserData)
.Bind(SaveUser)
.Map(u => new UserDto(u.Id, u.Name));
// Comprehensive error handling
result.Match(
onSuccess: dto => ProcessSuccess(dto),
onFailure: error => LogAndHandle(error)
);Beyond simple null checks - comprehensive optional value handling:
// Safe value creation
var some = Option.Some(42);
var none = Option.None<int>();
// Complex transformation chains
var result = GetUser()
.Map(u => u.Profile)
.Bind(p => p.GetPreference("theme"))
.Filter(theme => IsValidTheme(theme))
.Map(theme => ApplyTheme(theme))
.GetValueOrDefault(DefaultTheme);Professional-grade exception management with advanced patterns:
// Basic exception handling
var result = Try.Execute(() => RiskyDatabaseOperation());
// Advanced retry with exponential backoff
var resilientResult = Try.Execute(() => CallExternalService())
.ExecuteWithRetry(
maxAttempts: 5,
delayMs: 1000,
backoffMultiplier: 2.0
);
// Circuit breaker pattern
var protectedResult = Try.Execute(() => UnreliableService())
.ExecuteWithCircuitBreaker(
failureThreshold: 10,
timeoutMs: 60000
);
// Composite operations with contextual error handling
var complexResult = Try.ExecuteCompositeOperation(
() => ValidateInput(),
() => ProcessData(),
() => SaveResults()
).ExecuteWithContextualErrorHandling("User registration process");Perfect for representing success/failure or valid/invalid states:
// Validation scenarios
Either<ValidationErrors, ValidUser> ValidateUser(UserInput input)
{
var errors = new List<string>();
if (string.IsNullOrEmpty(input.Email))
errors.Add("Email required");
if (input.Age < 18)
errors.Add("Must be adult");
return errors.Any()
? Either.Left<ValidationErrors, ValidUser>(new ValidationErrors(errors))
: Either.Right<ValidationErrors, ValidUser>(new ValidUser(input));
}
// Pattern matching
var processResult = ValidateUser(userInput)
.Match(
left: errors => HandleValidationErrors(errors),
right: validUser => ProcessValidUser(validUser)
);Declarative validation with fluent syntax:
// Method parameter validation
public User CreateUser(string name, int age, string email)
{
Ensure.That(name).IsNotNullOrEmpty("Name is required");
Ensure.That(age).IsGreaterThan(0, "Age must be positive");
Ensure.That(email).Satisfies(e => e.Contains("@"), "Invalid email format");
return new User(name, age, email);
}
// Complex object validation
var validatedOrder = Ensure.That(order)
.IsNotNull("Order cannot be null")
.Satisfies(o => o.Items.Any(), "Order must have items")
.Satisfies(o => o.Total > 0, "Order total must be positive")
.Satisfies(o => o.Customer != null, "Order must have customer")
.Value;
// Chaining validations
var processedUser = Ensure.That(user)
.IsNotNull()
.Satisfies(u => u.IsActive, "User must be active")
.Transform(u => u.NormalizeEmail())
.Satisfies(u => u.HasValidSubscription(), "Valid subscription required")
.Value;Build sophisticated data processing pipelines with enterprise-grade error handling:
// Complex business logic pipeline
var userRegistrationPipeline = Pipeline
.Start<UserRegistrationRequest>()
.Then(ValidateUserInput)
.Then(CheckUserUniqueness)
.Then(HashPassword)
.Then(CreateUserEntity)
.Then(SaveUserToDatabase)
.Then(SendWelcomeEmail)
.Then(GenerateUserToken);
var result = await userRegistrationPipeline.ExecuteAsync(registrationRequest);
// Data transformation pipeline with error accumulation
var dataProcessingPipeline = Pipeline
.Start<RawDataInput>()
.Then(ValidateDataFormat)
.Then(NormalizeData)
.Then(EnrichWithMetadata)
.Then(ApplyBusinessRules)
.Then(TransformToOutputFormat)
.ThenAsync(SaveToStorage);
// Parallel processing with error collection
var batchProcessingResult = await Pipeline
.StartBatch(dataItems)
.ProcessInParallel(item => ProcessSingleItem(item))
.CollectResults()
.HandleErrors(errors => LogBatchErrors(errors));public User GetUser(int id)
{
var user = database.Find(id);
if (user == null)
throw new UserNotFoundException();
if (!user.IsActive)
throw new UserInactiveException();
return user;
}public Result<User> GetUser(int id)
{
return Ensure.That(id).IsGreaterThan(0, "Invalid user ID")
.Transform(_ => database.Find(id))
.ToResult("User not found")
.Bind(ValidateUserActive)
.Bind(EnrichUserData);
}
private Result<User> ValidateUserActive(User user)
{
return user.IsActive
? Result.Success(user)
: Result.Failure("User account is deactivated");
}
private Result<User> EnrichUserData(User user)
{
return Try.Execute(() => userService.LoadUserProfile(user))
.ToResult("Failed to load user profile")
.Map(profile => user.WithProfile(profile));
}public async Task<Result<ProcessingResult>> ProcessComplexWorkflow(WorkflowRequest request)
{
return await Result.Success(request)
.Bind(ValidateWorkflowRequest)
.BindAsync(async req => await ExecuteWorkflowSteps(req))
.Bind(ValidateWorkflowResults)
.MapAsync(async results => await FinalizeWorkflow(results))
.Match(
onSuccess: result =>
{
logger.LogInformation("Workflow completed successfully: {WorkflowId}", result.Id);
return Result.Success(result);
},
onFailure: error =>
{
logger.LogError("Workflow failed: {Error}", error);
return Result.Failure<ProcessingResult>($"Workflow processing failed: {error}");
}
);
}Success<T>(T value) - Create a successful resultFailure<T>(string error) - Create a failed resultMap<TResult>(Func<T, TResult> func) - Transform the success valueBind<TResult>(Func<T, Result<TResult>> func) - Chain operations with error propagationMatch<TResult>(Func<T, TResult> onSuccess, Func<string, TResult> onFailure) - Pattern matchingTap(Action<T> action) - Execute side effects without changing the resultTapError(Action<string> action) - Execute side effects on errorIsSuccess - Check if result is successfulIsFailure - Check if result has failedValue - Get the success value (throws if failed)Error - Get the error message (empty if successful)Some<T>(T value) - Create an option with valueNone<T>() - Create an empty optionMap<TResult>(Func<T, TResult> func) - Transform the value if presentBind<TResult>(Func<T, Option<TResult>> func) - Chain operationsFilter(Func<T, bool> predicate) - Filter based on conditionMatch<TResult>(Func<T, TResult> onSome, Func<TResult> onNone) - Pattern matchingGetValueOrDefault(T defaultValue) - Get value or defaultOrElse(Func<Option<T>> alternative) - Provide alternative optionHasValue - Check if option has a valueValue - Get the value (throws if empty)Execute<T>(Func<T> func) - Execute function with exception handlingExecuteAsync<T>(Func<Task<T>> func) - Execute async function with exception handlingExecuteWithRetry<T>(Func<T> func, int maxAttempts, int delayMs) - Execute with retry logicExecuteWithCircuitBreaker<T>(Func<T> func, int failureThreshold, int timeoutMs) - Execute with circuit breakerExecuteCompositeOperation<T>(params Func<T>[] operations) - Execute multiple operations as compositeMap<TResult>(Func<T, TResult> func) - Transform success valueMatch<TResult>(Func<T, TResult> onSuccess, Func<Exception, TResult> onFailure) - Pattern matchingLeft<TLeft, TRight>(TLeft left) - Create left-side eitherRight<TLeft, TRight>(TRight right) - Create right-side eitherMatch<TResult>(Func<TLeft, TResult> onLeft, Func<TRight, TResult> onRight) - Pattern matchingMapLeft<TLeftResult>(Func<TLeft, TLeftResult> func) - Transform left valueMapRight<TRightResult>(Func<TRight, TRightResult> func) - Transform right valueIsLeft - Check if either is left-sideIsRight - Check if either is right-sideThat<T>(T value) - Start validation chainIsNotNull(string message = null) - Ensure value is not nullIsNotNullOrEmpty(string message = null) - Ensure string is not null or emptySatisfies(Func<T, bool> predicate, string message) - Custom validationIsGreaterThan<TComparable>(TComparable threshold, string message) - Compare valuesIsInRange<TComparable>(TComparable min, TComparable max, string message) - Range validationTransform<TResult>(Func<T, TResult> transform) - Transform validated valueValue - Get the validated value (throws if validation failed)// Nullable conversions
string? nullable = GetNullableString();
var option = nullable.ToOption();
var result = option.ToResult("Value was null");
// Async operations
var asyncResult = await GetUserAsync(id)
.MapAsync(user => user.Email)
.BindAsync(ValidateEmailAsync)
.TapAsync(email => LogEmailValidation(email));
// Collection operations
var results = await items
.SelectAsync(ProcessItemAsync)
.WhereSuccess()
.CollectResults();
// Entity Framework integrations
var user = await dbContext.Users
.FirstOrDefaultAsync(u => u.Id == id)
.ToResult("User not found")
.Bind(ValidateUserPermissions);MonadicSharp is protected under LGPL-3.0-or-later license with comprehensive intellectual property safeguards:
📋 Legal Note: This library includes sophisticated anti-piracy and intellectual property protection measures. Unauthorized copying, modification, or redistribution may result in legal action. For commercial licensing inquiries, please contact the author.
For complete licensing details, see LICENSE and Documentation/ATTRIBUTION.md.
We welcome contributions from the community! Here's how you can help:
MonadicSharp follows these core principles:
"To bring the elegance and safety of functional programming to the C# ecosystem, making robust error handling and null safety accessible to every .NET developer."
Made with ❤️ and lots of ☕ by Danny4897
"Write code that handles failure gracefully, and success will follow."