Meigs2.Functional 0.1.1-alpha16

This is a prerelease version of Meigs2.Functional.
There is a newer prerelease version of this package available.
See the version list below for details.
dotnet add package Meigs2.Functional --version 0.1.1-alpha16
NuGet\Install-Package Meigs2.Functional -Version 0.1.1-alpha16
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="Meigs2.Functional" Version="0.1.1-alpha16" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Meigs2.Functional --version 0.1.1-alpha16
#r "nuget: Meigs2.Functional, 0.1.1-alpha16"
#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.
// Install Meigs2.Functional as a Cake Addin
#addin nuget:?package=Meigs2.Functional&version=0.1.1-alpha16&prerelease

// Install Meigs2.Functional as a Cake Tool
#tool nuget:?package=Meigs2.Functional&version=0.1.1-alpha16&prerelease

Functional: A Functional Programming Library for C#

A word of caution: Functional is currently a work in progress, and development is being done directly on the master branch, so the API can and will change at any time, without notice. Should you use this library at the present time, do so at your own risk!

What is Functional?

Functional is my personal library of extensions, functions, objects and structures for writing functional code in C#. It's an amalgamation of the LaYumba.Functional,language-ext and CSharpFunctionalExtensions libraries copied, modified and extended to suit my own needs.

Why?

After reading the wonderful Functional Programming in C# by Enrico Buonanno (unaffiliated) I saw how functional programming concepts, techniques and structures could be extremely useful in my code bases. There are several libraries that exist currently that implement FP concepts in .NET for C#, however I found that not all of them quite had the structure or functionality that I was looking for, so instead I am creating, combining, modifying and extending these libraries, along with implementing my own structures and functions, to create a functional utility library that I can use in my own projects.

Functional is very "opinionated" and is used in several of my projects. It is not optimized for performance, but rather for readability and maintainability. It is not intended to be a "drop-in" replacement for the existing libraries, but rather a combination of them, with some of my own ideas and implementations.

What's in it?

Some of the features from Functional that I use most often are:

  • Result and Result<T> types for better handling of errors, expected, unexpected, exceptional and otherwise compared to throwing exceptions
  • Option and Option<T> types for better null and empty handling
  • ValueObject<T> for creating typed value objects
  • Enumeration<T> and FlaggedEnumeration<T> for creating strongly typed static enumeration value types, similar to ValueObject<T> but for enumerations
    • DynamicEnumeration<T> for creating dynamic enumeration value types which can add new values at runtime
  • Functional.F static class
  • Several useful structures and monads from functional programming, like Identity<T>, Option<T>, Either<TLeft, TRight>
  • Extensions to IEumerable<T> and IEnumerable
  • And more!

Installation

This library is not current available on NuGet, however once a stable version is released it will be.

At the moment, I choose to git submodule this library into my projects, but you can also just copy the source files into your project.

Usage

Reference Functional.Core in your project, and you're good to go!

Should you want to use the static functions, add:

using static Functional.Core.F;

to your file.

If using C# 10, you can optionally add a global using static Functional.Core.F; to your project file to make the static available everywhere. If using C# 10, you can optionally add a Usings.cs file and add:

global using static Functional.Core.F;

to the file to make the static methods available everywhere within your project.

Basic Feature Overview:

This is a quick overview of some of the features of Functional. For more information, please see the wiki.

Most of the structures of Functional have some sort of "base" class (Result for Result<T>, Option for Option<T>, etc) which define many implicit operations to make working with them easier. For example, Option<T> as a conversion to and from T. This allows for flexable and clear code, such as:

Option<int> option = Some(5);
int value = option;

public Option<int> GetSome() => 5; // 5 -> Some(5)
// or
public Option<int> GetNone() => Option.None; // None -> Option<int>

int value = GetSome().Value; // Some(5) -> 5

int value = GetNone(); // None -> default(int) -> 0
int value = GetNone().ValueOrThrow(); // None -> throws InvalidOperationException

Result and Result<T>

Why use Result instead of returning null? Why not Try...(.., ref T result)? Throw an exception?

In traditional C# programming, we have a few options for handling errors:

  • Return null or default(T) and hope that the caller checks for it
  • Return a bool or enum to indicate success or failure, and return the result via an out or ref parameter (Try...(.., ref T result) pattern)
  • Throw an exception

All of these options have their own problems:

  • Returning null or default(T) can lead to ambiguity as to the actual result of the operation (is null a valid result, or an error?) and can lead to NullReferenceExceptions if the caller doesn't check for it.
  • Returning a bool or enum is not descriptive can throw away useful information about the error
  • Throwing an exception is costly, and exceptions should (ideally) only be used for actual exceptional circumstances, not for expected errors.
  • None of these options allow for the composition of errors.

The Result and various Error fixes many of these problems:

  • A Result is either a Success or an Failure, and is composed of a list of Errors
    • Any given Result is a Success if and only if it has no Errors which are not expected
    • Functions which define a sequence of operations no longer need to check for errors after each operation, but can instead compose the results of each operation into a single Result.
    • A Result is a monad, which allows for functional composition of operations using Bind, Map, Apply, etc.
Sample Usage of Result and Result<T>
// Create a Result
var result = Result.Success;

// Create a Result<T>
var result = Result.Success(42);

// Create a Result with an Error
var result = Result.Failure("Something went wrong"); // <- implicit conversion from string -> Error

// Create a Result<T> with an Error
var result = Result.Failure<int>("Something went wrong"); // <- implicit conversion from string -> Error

// Create a Result with multiple Errors
var result = Result.Failure("Something went wrong", "Something else went wrong"); // <- implicit conversion from string -> Error

// Combine Results
var result = Result.Combine(Result.Success(), Result.Success()); // <- Success
var result = Result.Combine(Result.Success(), "Something went wrong"); // <- Failure

// Add a Result to another Result
var result = Result.Success; // <- Success
result += Result.Success(43); // <- Success
result += Result.Failure("Something went wrong"); // <- Failure

// Add an Error to a Result
var result = Result.Success; // <- Success
result += "Something went wrong"; // <- Failure
result.WithError("Something else went wrong"); // <- Failure

// Implicit conversion within method to Result<T>
public Result<int> GetNumber() => 42; // <- Success(42)

public Result<int> GreaterThanZero(int number)
{
    // Implicit conversion from int to Result<T>
    if (number > 0) return number; // <- Success(number)
    else return "Number must be greater than zero"; // <- Implicit conversion from string -> Result<int> (Failure)
}
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  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. 
.NET Core netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.1 is compatible. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
0.1.1-alpha29 84 6/24/2023
0.1.1-alpha28 87 4/23/2023
0.1.1-alpha27 104 3/18/2023
0.1.1-alpha26 90 3/18/2023
0.1.1-alpha23 97 12/30/2022
0.1.1-alpha21 96 12/17/2022
0.1.1-alpha20 105 11/13/2022
0.1.1-alpha19 95 11/12/2022
0.1.1-alpha18 94 11/12/2022
0.1.1-alpha17 89 11/6/2022
0.1.1-alpha16 111 11/5/2022