Getting Started
DarkPeak.Functional is a functional programming library for .NET that provides monadic types and railway-oriented programming patterns.
Installation
dotnet add package DarkPeak.Functional
Core Concepts
Core Functional Types
The library provides four monadic types plus OneOf, each serving a different purpose:
| Type | Purpose | Error Handling |
|---|---|---|
Option<T> |
Represents a value that may or may not exist | No error info — just presence/absence |
Result<T, TError> |
Represents success or failure | Single error, short-circuits on first failure |
Either<TLeft, TRight> |
Represents one of two possible values | No bias — both sides are equally valid |
OneOf<T1, ..., Tn> |
Represents one of 2-8 possible values | No bias — every case is explicit and valid |
Validation<T, TError> |
Represents valid or invalid with errors | Accumulates all errors |
Railway-Oriented Programming
The monadic types share a common set of operations that let you chain computations without checking for errors at every step:
- Map — Transform the value inside the container
- Bind — Chain operations that themselves return a container
- Match — Extract the final result by handling all cases
using DarkPeak.Functional;
// Map transforms the inner value
var doubled = Option.Some(21).Map(x => x * 2); // Some(42)
// Bind chains operations that return Options
var result = Option.Some("42")
.Bind(s => Option.TryParse<int>(s))
.Map(x => x * 2); // Some(84)
// Match extracts the value by handling all cases
var message = result.Match(
some: x => $"Got {x}",
none: () => "Nothing"); // "Got 84"
LINQ Support
The monadic types, plus OneOf, support LINQ query syntax:
var result =
from x in Option.Some(10)
from y in Option.Some(20)
where x + y > 25
select x + y; // Some(30)
Async Support
Every operation has an async variant suffixed with Async:
var result = await Option.Some("https://api.example.com")
.BindAsync(url => FetchDataAsync(url))
.MapAsync(data => ParseAsync(data));
Task Extensions
Fluent async chaining is available via task extensions — no intermediate await needed:
using DarkPeak.Functional.Extensions;
var result = await FetchUserAsync(userId) // Task<Result<User, Error>>
.Map(user => user.Email) // Task<Result<string, Error>>
.Bind(email => ValidateEmailAsync(email)) // Task<Result<Email, Error>>
.Match(
success: email => $"Valid: {email}",
failure: err => $"Error: {err.Message}");
Namespace Layout
DarkPeak.Functional — Core types (Option, Result, Either, OneOf, Validation, Error)
DarkPeak.Functional.Extensions — Extension methods (type conversions, task extensions, etc.)