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 modern functional programming library for C# featuring Result, Option, Railway-Oriented Programming, and pipeline composition. Build robust applications with elegant error handling and monadic patterns.
dotnet add package MonadicSharpusing MonadicSharp;
using MonadicSharp.Extensions;
// Result<T> for error handling
var result = Result.Success(42)
.Map(x => x * 2)
.Bind(x => x > 50 ? Result.Success(x) : Result.Failure("Too small"))
.Map(x => $"Final value: {x}");
result.Match(
onSuccess: value => Console.WriteLine(value),
onFailure: error => Console.WriteLine($"Error: {error}")
);
// Option<T> for null-safe operations
var user = GetUser()
.Map(u => u.Email)
.Filter(email => email.Contains("@"))
.GetValueOrDefault("no-email@example.com");Get 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
Railway-oriented programming for elegant error handling:
// Success path
var success = Result.Success("Hello World");
// Failure path
var failure = Result.Failure<string>("Something went wrong");
// Chaining operations
var result = GetUser(id)
.Bind(ValidateUser)
.Bind(SaveUser)
.Map(u => u.Id);Null-safe operations without null reference exceptions:
// Some value
var some = Option.Some(42);
// No value
var none = Option.None<int>();
// Safe operations
var result = GetUser()
.Map(u => u.Name)
.Filter(name => name.Length > 0)
.GetValueOrDefault("Anonymous");Build complex data processing pipelines:
var pipeline = Pipeline
.Start<string>()
.Then(ParseNumber)
.Then(ValidateRange)
.Then(FormatOutput);
var result = await pipeline.ExecuteAsync("42");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 database.Find(id)
.ToResult("User not found")
.Bind(ValidateUserActive);
}
private Result<User> ValidateUserActive(User user)
{
return user.IsActive
? Result.Success(user)
: Result.Failure("User is inactive");
}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 operationsMatch<TResult>(Func<T, TResult> onSuccess, Func<string, TResult> onFailure) - Pattern matchIsSuccess - 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 matchGetValueOrDefault(T defaultValue) - Get value or defaultHasValue - Check if option has a valueValue - Get the value (throws if empty)// Convert nullable to Option
string? nullable = GetNullableString();
var option = nullable.ToOption();
// Convert to Result
var result = option.ToResult("Value was null");
// Async operations
var asyncResult = await GetUserAsync(id)
.MapAsync(user => user.Email)
.BindAsync(ValidateEmailAsync);This project is licensed under the MIT License - see the LICENSE file for details.
Contributions are welcome! Please feel free to submit a Pull Request.
Made with ❤️ by Danny4897
Una libreria .NET che fornisce implementazioni di tipi monadici e funzionali per C#.
Option<T> per gestire valori opzionaliResult<T> per gestire operazioni che possono fallireTaskdotnet add package MonadicSharp// Creazione di un Option
var someValue = Option<int>.Some(42);
var noneValue = Option<int>.None;
// Pattern matching
var result = someValue.Match(
some: value => $"Il valore è {value}",
none: () => "Nessun valore"
);
// Map e Bind
var doubled = someValue.Map(x => x * 2);
var maybeString = someValue.Bind(x => x > 0 ? Option<string>.Some(x.ToString()) : Option<string>.None);// Creazione di un Result
var success = Result<int>.Success(42);
var failure = Result<int>.Failure(Error.Create("Operazione fallita"));
// Pattern matching
var result = success.Match(
success: value => $"Operazione riuscita: {value}",
failure: error => $"Errore: {error.Message}"
);
// Map e Bind
var doubled = success.Map(x => x * 2);
var maybeString = success.Bind(x => x > 0 ? Result<string>.Success(x.ToString()) : Result<string>.Failure(Error.Create("Valore non valido")));// Creazione di un utente con validazione
public async Task<Result<User>> CreateUserAsync(UserDto userDto)
{
return await Result<UserDto>.Success(userDto)
.Map(ValidateUserDto)
.Bind(MapToUser)
.Bind(user => _userRepository.Users.AddAsync(user))
.BindAsync(SaveChangesAsync)
.Success(user => Result<User>.Success(user))
.Failure(error => Result<User>.Failure(error));
}
// Recupero di un utente con gestione errori
public async Task<Result<User>> GetUserByIdAsync(int id)
{
return await Result<int>.Success(id)
.Bind(id => _userRepository.Users.FindAsync(id))
.BindAsync(user => user.ToResult("Utente non trovato"))
.Success(user => Result<User>.Success(user))
.Failure(error => Result<User>.Failure(error));
}
// Aggiornamento di un utente con validazione
public async Task<Result<User>> UpdateUserAsync(int id, UserDto userDto)
{
return await Result<(int id, UserDto dto)>.Success((id, userDto))
.Map(tuple => (tuple.id, ValidateUserDto(tuple.dto)))
.Bind(tuple => _userRepository.Users.FindAsync(tuple.id)
.BindAsync(user => user.ToResult("Utente non trovato"))
.Bind(user => MapToUser(tuple.dto).Map(dto => (user, dto))))
.Bind(tuple => _userRepository.Users.Update(tuple.user))
.BindAsync(SaveChangesAsync)
.Success(user => Result<User>.Success(user))
.Failure(error => Result<User>.Failure(error));
}// Combinazione di operazioni asincrone
public async Task<Result<User>> GetUserWithDetailsAsync(int userId)
{
return await Result<int>.Success(userId)
.BindAsync(async id =>
{
var user = await _userRepository.Users.FindAsync(id);
var roles = await _userRepository.Roles.WhereAsync(r => r.UserId == id);
return (user, roles);
})
.BindAsync(async tuple =>
{
var user = await tuple.user.ToResult("Utente non trovato");
var roles = await tuple.roles;
return Result<User>.Success(user with { Roles = roles });
});
}Le contribuzioni sono benvenute! Per favore, apri una issue per discutere i cambiamenti che vorresti fare.
Questo progetto è licenziato sotto la licenza MIT - vedi il file LICENSE per i dettagli.