Skip to content

ApacheTech-Nuget-Packages/ApacheTech.Common.FunctionalCSharp

Repository files navigation

Functional C# - End-Game LINQ Library

A comprehensive C# library that brings practical functional programming tools to everyday .NET development. It provides monads, discriminated unions, memoisation helpers, currying and partial application utilities, and a small set of LINQ-style extensions to make common functional patterns concise and expressive.

Support the Author

If you find this library useful and would like to support its continued development, please consider one of the options below. Your support helps fund maintenance, documentation and new features.


Installation

Install the package from NuGet:

dotnet add package ApacheTech.Common.FunctionalCSharp

Alternatively, add the ApacheTech.Common.FunctionalCSharp project to your solution and reference it directly during development.


Key Concepts

This section summarises the principal APIs and how they are intended to be used.

Monads

Monads in this library are pragmatic tools for modelling optional values and fallible computations.

  • Maybe<T> represents an optional value. Use Actual<T> for present values and Nothing<T> for absence. Companion extensions such as ToMaybe() and Bind() make chaining straightforward and safe.

  • Identity<T> is a minimal wrapper that carries a value and supports implicit conversions. It is useful when a monadic interface is desired without special semantics for missing values.

  • Attempt<T> models computations that may fail. Success<T> contains the result; Fail<T> contains an exception and short-circuits subsequent operations.

Usage guidance: prefer Maybe where absence is a valid state and Attempt where exceptions should be modelled explicitly in the computation chain.

OneOf — discriminated unions

OneOf<T1, T2> holds one of two possible types. Use Match or Invoke to handle each case explicitly. This pattern is useful when a function may return one of a small set of distinct types (for example, a result or an error value) and you want callers to handle both cases explicitly.

Memento (memoisation)

Memento<TInput, TOutput> decorates a pure function with a dictionary-backed cache. It offers implicit conversions to and from Func<TInput, TOutput> to ease substitution of decorated and plain functions.

Important caveats: use only for pure, deterministic functions, and ensure TInput is a suitable dictionary key (immutable or with stable equality semantics).

Functional helpers (Apply & Curry)

  • Apply (partial application) binds leading arguments of a function to produce a function of smaller arity, which can be useful for creating specialised functions.
  • Curry converts a multi-argument function into nested single-argument functions, facilitating composition and reuse in functional pipelines.

Both families include overloads for a broad range of arities.

Monadic LINQ-style helpers

  • Map applies a transformation to a value and returns the result.
  • Alt attempts a primary mapping and uses a fallback when the primary result is a default value.
  • Fork executes multiple independent paths against the same input and combines the results using a provided join function.
  • TryValidate returns whether all predicates hold and provides the set of failing rules; Validate simply returns a boolean.

These helpers aim to make common patterns succinct and readable.

Try/catch helpers & object utilities

A small set of helpers reduces boilerplate around common error handling patterns. TryCatch<TException> wraps an Action with a handler for the specified exception type; convenience variants include TryOrFailFast, TryOrThrow, TryOrLogToConsole and TryOrLogToDebug.

IsDefaultValue<T> tests whether a value equals the default for its type and is useful for concise default checks.


Examples / Recipes

This section contains compact examples illustrating typical usage patterns.

Chaining fallible computations

Parse, transform and format only when each step succeeds. The chain short-circuits on failure.

using ApacheTech.Common.FunctionalCSharp.Monads;
using ApacheTech.Common.FunctionalCSharp.Monads.Extensions;

var formatted = "123"
    .ToMaybe()
    .Bind(i => (i * 2).ToMaybe())
    .Map(i => i.ToString());

Handling multiple result types with OneOf

Use OneOf to return one of two types and handle both cases explicitly.

var result = new OneOf<int, string>(404);
string message = result.Match(
    code => $"Error code: {code}",
    text => $"Message: {text}"
);

Memoising an expensive pure function

Decorate a pure function with Memento to cache results for repeated inputs.

Func<string, string> computeKey = s => s.ToUpperInvariant(); // expensive
var memoKey = new Memento<string, string>(computeKey);
var k1 = memoKey.Invoke("abc"); // computed
var k2 = memoKey.Invoke("abc"); // cached

Validation helpers

Run multiple predicates and inspect failures.

var user = new User { Name = "", Age = 12 };
var ok = user.TryValidate(out var failed,
    u => !string.IsNullOrWhiteSpace(u.Name),
    u => u.Age >= 13);

if (!ok)
{
    foreach (var rule in failed) Console.WriteLine("Validation failed");
}

Contributing

Contributions, bug reports and improvements are welcome. Please open issues or pull requests against the GitHub repository. When contributing, please follow the existing code style and add unit tests for new behaviours.

  • Run dotnet test in the test project before submitting changes.

Licence

This project is released under the terms of the licence included in LICENCE.md.

About

Functional programming extension methods and helpers for C#

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published