Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/vkhorikov/CSharpFunctionalExtensions
Functional extensions for C#
https://github.com/vkhorikov/CSharpFunctionalExtensions
csharp entity functional-programming maybe-monad result value-object
Last synced: 12 days ago
JSON representation
Functional extensions for C#
- Host: GitHub
- URL: https://github.com/vkhorikov/CSharpFunctionalExtensions
- Owner: vkhorikov
- License: mit
- Created: 2016-06-18T08:50:27.000Z (over 8 years ago)
- Default Branch: master
- Last Pushed: 2024-10-01T13:45:24.000Z (about 1 month ago)
- Last Synced: 2024-10-10T14:21:33.463Z (29 days ago)
- Topics: csharp, entity, functional-programming, maybe-monad, result, value-object
- Language: C#
- Homepage:
- Size: 1.58 MB
- Stars: 2,408
- Watchers: 83
- Forks: 303
- Open Issues: 99
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- awesome - vkhorikov/CSharpFunctionalExtensions - Functional extensions for C# (C\#)
- awesome-dotnet-core - CSharpFunctionalExtensions - Functional Extensions for C#. (Frameworks, Libraries and Tools / Functional Programming)
- awesome-reference-tools - CSharpFunctionalExtensions
- fucking-awesome-dotnet-core - CSharpFunctionalExtensions - Functional Extensions for C#. (Frameworks, Libraries and Tools / Functional Programming)
- awesome-dotnet - CSharpFunctionalExtensions - Functional extensions for C# (Libraries, Frameworks and Tools / Functional Programming)
- awesome-dotnet-core - CSharpFunctionalExtensions - Functional Extensions for C#. (Frameworks, Libraries and Tools / Functional Programming)
- awesome-dotnet-core - CSharpFunctionalExtensions - C#的功能扩展。 (框架, 库和工具 / 响应式编程)
README
# Functional Extensions for C#
[![Build Status](https://dev.azure.com/EnterpriseCraftsmanship/CSharpFunctionalExtensions/_apis/build/status/CSharpFunctionalExtensions?branchName=master)](https://dev.azure.com/EnterpriseCraftsmanship/CSharpFunctionalExtensions/_build/latest?definitionId=1&branchName=master)
[![NuGet downloads](https://img.shields.io/nuget/v/csharpfunctionalextensions.svg)](https://www.nuget.org/packages/CSharpFunctionalExtensions/)
[![GitHub license](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/vkhorikov/CSharpFunctionalExtensions/blob/master/LICENSE)This library helps write code in more functional way.
To get to know more about the principles behind it, check out the [Applying Functional Principles in C# Pluralsight course](https://enterprisecraftsmanship.com/ps-func).## Installation
Available on [NuGet](https://www.nuget.org/packages/CSharpFunctionalExtensions/)
```bash
dotnet add package CSharpFunctionalExtensions
```or
```powershell
PM> Install-Package CSharpFunctionalExtensions
```Also available as a strong named assembly (big thanks to [bothzoli](https://github.com/bothzoli) who made it possible!).
On [NuGet](https://www.nuget.org/packages/CSharpFunctionalExtensions.StrongName/)
```bash
dotnet add package CSharpFunctionalExtensions.StrongName
```## Core Concepts
### Get rid of primitive obsession
```csharp
Result name = CustomerName.Create(model.Name);
Result email = Email.Create(model.PrimaryEmail);Result result = Result.Combine(name, email);
if (result.IsFailure)
return Error(result.Error);var customer = new Customer(name.Value, email.Value);
```## Make nulls explicit with the Maybe type
```csharp
Maybe customerOrNothing = _customerRepository.GetById(id);
if (customerOrNothing.HasNoValue)
return Error("Customer with such Id is not found: " + id);
```## Compose multiple operations in a single chain
```csharp
return _customerRepository.GetById(id)
.ToResult("Customer with such Id is not found: " + id)
.Ensure(customer => customer.CanBePromoted(), "The customer has the highest status possible")
.Tap(customer => customer.Promote())
.Tap(customer => _emailGateway.SendPromotionNotification(customer.PrimaryEmail, customer.Status))
.Finally(result => result.IsSuccess ? Ok() : Error(result.Error));
```## Wrap multiple operations in a TransactionScope
```csharp
return _customerRepository.GetById(id)
.ToResult("Customer with such Id is not found: " + id)
.Ensure(customer => customer.CanBePromoted(), "The customer has the highest status possible")
.WithTransactionScope(customer => Result.Success(customer)
.Tap(customer => customer.Promote())
.Tap(customer => customer.ClearAppointments()))
.Tap(customer => _emailGateway.SendPromotionNotification(customer.PrimaryEmail, customer.Status))
.Finally(result => result.IsSuccess ? Ok() : Error(result.Error));
```## API Examples
### Maybe
#### Explicit Construction
Use case: Creating a new Maybe containing a value
```csharp
Maybe apple = Maybe.From("apple");// or
Maybe apple = Maybe.From("apple"); // type inference
// or
var apple = Maybe.From("apple");
```#### None/No Value
Use case: Replacing `null` or the
[Null Object Pattern](https://enterprisecraftsmanship.com/2015/03/13/functional-c-non-nullable-reference-types/) for representing 'missing' data.```csharp
int storeInventory = ...Maybe fruit = storeInventory > 0
? Maybe.From("apple")
: Maybe.None;// or where the generic type is a reference type
Maybe fruit = null;
// or where the generic type is a value type
Maybe fruit = default;
```#### Implicit Conversion
Use case: Easily creating a Maybe from a value
```csharp
// Constructing a Maybe
Maybe apple = "apple"; // implicit conversion// Or as a method return value
Maybe GetFruit(string fruit)
{
if (string.IsNullOrWhiteSpace(fruit))
{
return Maybe.None;
}return fruit; // implicit conversion
}
```#### Equality
Use case: Comparing Maybes or values without knowledge of the
inner value of the Maybes```csharp
Maybe apple = "apple";
Maybe orange = "orange";
string alsoOrange = "orange";
Maybe noFruit = Maybe.None;Console.WriteLine(apple == orange); // false
Console.WriteLine(apple != orange); // true
Console.WriteLine(orange == alsoOrange); // true
Console.WriteLine(alsoOrange == noFruit); // false
```#### ToString
```csharp
Maybe apple = "apple";
Maybe noFruit = Maybe.None;Console.WriteLine(apple.ToString()); // "apple"
Console.WriteLine(noFruit.ToString()); // "No value"
```#### GetValueOrThrow
Use case: Procedurally accessing the inner value of the Maybe
**Note**: Calling this will throw a `InvalidOperationException` if there is no value
```csharp
Maybe apple = "apple";
Maybe noFruit = Maybe.None;Console.WriteLine(apple.GetValueOrThrow()); // "apple";
Console.WriteLine(noFruit.GetValueOrThrow()); // throws InvalidOperationException !!
Console.WriteLine(noFruit.GetValueOrThrow(new CustomException())); // throws CustomException !!
```#### HasValue and HasNoValue
Use case: Procedurally checking if the Maybe has a value,
usually before accessing the value directly```csharp
void Response(string fruit)
{
Console.WriteLine($"Yum, a {fruit} 😀");
}Maybe apple = "apple";
Maybe noFruit = Maybe.None;if (apple.HasValue)
{
Response(apple.Value); // safe to access since we checked above
}if (noFruit.HasNoValue)
{
Response("We're all out of fruit 😢");
}
```#### GetValueOrDefault
Use case: Safely accessing the inner value, without checking if there is one, by providing a fallback
if no value exists```csharp
void Response(string fruit)
{
Console.WriteLine($"It's a {fruit}");
}Maybe apple = "apple";
Maybe unknownFruit = Maybe.None;string appleValue = apple.GetValueOrDefault("banana");
string unknownFruitValue = unknownFruit.GetValueOrDefault("banana");Response(appleValue); // It's a apple
Response(unknownFruitValue); // It's a banana
```#### Where
Use case: Converting a Maybe with a value to a `Maybe.None` if a condition isn't met
**Note**: The predicate passed to `Where` (ex )
```csharp
bool IsMyFavorite(string fruit)
{
return fruit == "papaya";
}Maybe apple = "apple";
Maybe favoriteFruit = apple.Where(IsMyFavorite);
Console.WriteLine(favoriteFruit.ToString()); // "No value"
```#### Map
Use case: Transforming the value in the Maybe, if there is one, without
needing to check if the value is there**Note**: the delegate (ex `CreateMessage`) passed to `Maybe.Map()` is only executed if the Maybe has an inner value
```csharp
string CreateMessage(string fruit)
{
return $"The fruit is a {fruit}";
}Maybe apple = "apple";
Maybe noFruit = Maybe.None;Console.WriteLine(apple.Map(CreateMessage).Unwrap("No fruit")); // "The fruit is a apple"
Console.WriteLine(noFruit.Map(CreateMessage).Unwrap("No fruit")); // "No fruit"
```#### Select
**Alias**: `Maybe.Select()` is an alias of `Maybe.Map()`
#### Bind
Use case: Transforming from one Maybe into another Maybe
(like `Maybe.Map` but it transforms the Maybe instead of the inner value)**Note**: the delegate (ex `MakeAppleSauce`) passed to `Maybe.Bind()` is only executed if the Maybe has an inner value
```csharp
Maybe MakeAppleSauce(Maybe fruit)
{
if (fruit == "apple") // we can only make applesauce from apples 🍎
{
return "applesauce";
}return Maybe.None;
}Maybe apple = "apple";
Maybe banana = "banana";
Maybe noFruit = Maybe.None;Console.WriteLine(apple.Bind(MakeAppleSauce).ToString()); // "applesauce"
Console.WriteLine(banana.Bind(MakeAppleSauce).ToString()); // "No value"
Console.WriteLine(noFruit.Bind(MakeAppleSauce).ToString()); // "No value"
```#### SelectMany
**Alias**: `Maybe.SelectMany()` is an alias of `Maybe.Bind()`
#### Choose
Use case: Filter a collection of Maybes to only the ones that have a value,
and then return the value for each, or map that value to a new one**Note**: the delegate passed to `Maybe.Choose()` is only executed on the Maybes of the collection with an inner value
```csharp
IEnumerable> unknownFruits = new[] { "apple", Maybe.None, "banana" };IEnumerable knownFruits = unknownFruits.Choose();
IEnumerable fruitResponses = unknownFruits.Choose(fruit => $"Delicious {fruit}");Console.WriteLine(string.Join(", ", knownFruits)) // "apple, banana"
Console.WriteLine(string.Join(", ", fruitResponses)) // "Delicious apple, Delicious banana"
```#### Execute
Use case: Safely executing a `void` (or `Task`) returning operation on the Maybe inner value
without checking if there is one**Note**: the `Action` (ex `PrintFruit`) passed to `Maybe.Execute()` is only executed if the Maybe has an inner value
```csharp
void PrintFruit(string fruit)
{
Console.WriteLine($"This is a {fruit}");
}Maybe apple = "apple";
Maybe noFruit = Maybe.None;apple.Execute(PrintFruit); // "This is a apple"
noFruit.Execute(PrintFruit); // no output to the console
```#### ExecuteNoValue
Use case: Executing a `void` (or `Task`) returning operation when the Maybe has no value
```csharp
void LogNoFruit(string fruit)
{
Console.WriteLine($"There are no {fruit}");
}Maybe apple = "apple";
Maybe banana = Maybe.None;apple.ExecuteNoValue(() => LogNoFruit("apple")); // no output to console
banana.ExecuteNoValue(() => LogNoFruit("banana")); // "There are no banana"
```#### Or
Use case: Supplying a fallback value Maybe or value in the case that the Maybe has no inner value
**Note**: The fallback `Func` (ex `() => "banana"`) will only be executed
if the Maybe has no inner value```csharp
Maybe apple = "apple";
Maybe banana = "banana";
Maybe noFruit = Maybe.None;Console.WriteLine(apple.Or(banana).ToString()); // "apple"
Console.WriteLine(noFruit.Or(() => banana)).ToString()); // "banana"
Console.WriteLine(noFruit.Or("banana").ToString()); // "banana"
Console.WriteLine(noFruit.Or(() => "banana").ToString()); // "banana"
```#### Match
Use case: Defining two operations to perform on a Maybe.
One to be executed if there is an inner value, and the other to executed if there is not```csharp
Maybe apple = "apple";
Maybe noFruit = Maybe.None;// Void returning Match
apple.Match(
fruit => Console.WriteLine($"It's a {fruit}"),
() => Console.WriteLine("There's no fruit"));// Mapping Match
string fruitMessage = noFruit.Match(
fruit => $"It's a {fruit}",
() => "There's no fruit"));Console.WriteLine(fruitMessage); // "There's no fruit"
```#### TryFirst and TryLast
Use case: Replacing `.FirstOrDefault()` and `.LastOrDefault()` so that you can return a
Maybe instead of a `null` or value type default value (like `0`, `false`) when working with collections```csharp
IEnumerable fruits = new[] { "apple", "coconut", "banana" };Maybe firstFruit = fruits.TryFirst();
Maybe probablyABanana = fruits.TryFirst(fruit => fruit.StartsWith("ba"));
Maybe aPeachOrAPear = fruits.TryFirst(fruit => fruit.StartsWith("p"));Console.WriteLine(firstFruit.ToString()); // "apple"
Console.WriteLine(probablyABanana.ToString()); // "banana"
Console.WriteLine(aPeachOrAPear.ToString()); // "No value"Maybe lastFruit = fruits.TryLast();
Maybe anAppleOrApricot = fruits.TryLast(fruit => fruit.StartsWith("a"));Console.WriteLine(lastFruit.ToString()); // "banana"
Console.WriteLine(anAppleOrApricot.ToString()); // "apple"
```#### TryFind
Use case: Safely getting a value out of a Dictionary
```csharp
Dictionary fruitInventory = new()
{
{ "apple", 10 },
{ "banana", 2 }
};Maybe appleCount = fruitInventory.TryFind("apple");
Maybe kiwiCount = fruitInventory.TryFind("kiwi");Console.WriteLine(appleCount.ToString()); // "10"
Console.WriteLine(kiwiCount.ToString()); // "No value"
```#### ToResult
Use case: Representing the lack of an inner value in a Maybe as a failed operation
**Note**: See `Result` section below
```csharp
Maybe fruit = "banana";
Maybe noFruit = Maybe.None;string errorMessage = "There was no fruit to give";
Result weGotAFruit = fruit.ToResult(errorMessage);
Result failedToGetAFruit = noFruit.ToResult(errorMessage);Console.WriteLine(weGotAFruit.Value); // "banana"
Console.WriteLine(failedToGetAFruit.Error); // "There was no fruit to give"
```#### ToUnitResult
Use case: Representing the lack of an inner value in a Maybe as a failed operation, if an Error is provided
Use case: Representing the presence of an inner value in a Maybe as a failed operation
**Note**: See `UnitResult` section below
```csharp
Maybe error = new Error();
Maybe noFruit = Maybe.None;UnitResult weGotAnError = error.ToUnitResult();
UnitResult failedToGetAFruit = noFruit.ToUnitResult(new Error());Console.WriteLine(weGotAnError.IsFailure); // true
Console.WriteLine(failedToGetAFruit.IsFailure); // true
```### Result
#### Explicit Construction: Success and Failure
Use case: Creating a new Result in a Success or Failure state
```csharp
record FruitInventory(string Name, int Count);Result appleInventory = Result.Success(new FruitInventory("apple", 4));
Result failedOperation = Result.Failure("Could not find inventory");
Result successInventoryUpdate = Result.Success();
```To create a success result of a value you can also use the `Of` method which has overloads for `Func` and `Task`.
```csharp
Result something = Result.Of(_service.CreateSomething());
Result something = await Result.Of(_service.CreateSomethingAsync());
Result something = Result.Of(() => _service.CreateSomething());
Result something = await Result.Of(() => _service.CreateSomethingAsync());
```#### Conditional Construction: SuccessIf and FailureIf
Use case: Creating successful or failed Results based on expressions or delegates instead of if/else statements or ternary expressions
```csharp
bool onTropicalIsland = true;Result foundCoconut = Result.SuccessIf(onTropicalIsland, "These trees seem bare 🥥");
Result foundGrapes = Result.FailureIf(() => onTropicalIsland, "No grapes 🍇 here");// or
bool isNewShipmentDay = true;
Result appleInventory = Result.SuccessIf(isNewShipmentDay, new FruitInventory("apple", 4), "No 🍎 today");
Result bananaInventory = Result.SuccessIf(() => isNewShipmentDay, new FruitInventory("banana", 2), "All out of 🍌");// or
bool afterBreakfast = true;
Result orangeInventory = Result.FailureIf(afterBreakfast, new FruitInventory("orange", 10), "No 🍊 today");
Result grapefruitInventory = Result.FailureIf(() => afterBreakfast, new FruitInventory("grapefruit", 5), "No grapefruit 😢");
```#### Implicit Conversion
Use case: Easily creating a successful result from a value
```csharp
Result appleInventory = new FruitInventory("apple", 4);
Result failedInventoryUpdate = "Could not update inventory";
```#### ToString
Use case: Printing out the state of a Result and its inner value or error
```csharp
Result appleInventory = new FruitInventory("apple", 4);
Result bananaInventory = Result.Failure("Could not find any bananas");
Result failedInventoryUpdate = "Could not update inventory";
Result successfulInventoryUpdate = Result.Success();Console.WriteLine(appleInventory.ToString()); // "Success(FruitInventory { Name = apple, Count = 4 })"
Console.WriteLine(bananaInventory.ToString()); // "Failure(Could not find any bananas)"
Console.WriteLine(failedInventoryUpdate.ToString()); // "Failure(Could not update inventory)"
Console.WriteLine(successfulInventoryUpdate.ToString()); // "Success"
```#### Map
Use case: Transforming the inner value of a successful Result, without needing to check on
the success/failure state of the Result**Note**: the delegate (ex `CreateMessage`) passed to `Result.Map()` is only executed if the Result was successful
```csharp
string CreateMessage(FruitInventory inventory)
{
return $"There are {inventory.Count} {inventory.Name}(s)";
}Result appleInventory = new FruitInventory("apple", 4);
Result bananaInventory = Result.Failure("Could not find any bananas");Console.WriteLine(appleInventory.Map(CreateMessage).ToString()); // "Success(There are 4 apple(s))"
Console.WriteLine(bananaInventory.Map(CreateMessage).ToString()); // "Failure(Could not find any bananas)"
```#### MapError
Use case: Transforming the inner error of a failed Result, without needing to check on
the success/failure state of the Result**Note**: the delegate (ex `ErrorEnhancer`) passed to `Result.MapError()` is only executed if the Result failed
```csharp
string ErrorEnhancer(string errorMessage)
{
return $"Failed operation: {errorMessage}";
}Console.WriteLine(appleInventory.MapError(ErrorEnhancer).ToString()); // "Success(FruitInventory { Name = apple, Count = 4 })"
Console.WriteLine(bananaInventory.MapError(ErrorEnhancer).ToString()); // "Failed operation: Could not find any bananas"
```## Testing
### CSharpFunctionalExtensions.FluentAssertions
A small set of extensions to make test assertions more fluent when using CSharpFunctionalExtensions! Check out the [repo for this library](https://github.com/NitroDevs/CSharpFunctionalExtensions.FluentAssertions) more information!
Includes custom assertions for
- Maybe
- Result
- Result
- Result
- UnitResult#### Example
```csharp
var result = Result.Success(420);result.Should().Succeed(); // passes
result.Should().SucceedWith(420); // passes
result.Should().SucceedWith(69); // throws
result.Should().Fail(); // throws
```## Analyzers
### [CSharpFunctionalExtensions.Analyzers](https://github.com/AlmarAubel/CSharpFunctionalExtensions.Analyzers)
A Roslyn analyzer package that provides warnings and recommendations to prevent misuse of `Result` objects in `CSharpFunctionalExtensions`. Ensures more robust implementation when working with Result types.Available on [NuGet](https://www.nuget.org/packages/CSharpFunctionalExtensions.Analyzers)
```bash
dotnet add package CSharpFunctionalExtensions.Analyzers
```## Read or Watch more about these ideas
- [Functional C#: Primitive obsession](https://enterprisecraftsmanship.com/2015/03/07/functional-c-primitive-obsession/)
- [Functional C#: Non-nullable reference types](https://enterprisecraftsmanship.com/2015/03/13/functional-c-non-nullable-reference-types/)
- [Functional C#: Handling failures, input errors](https://enterprisecraftsmanship.com/2015/03/20/functional-c-handling-failures-input-errors/)
- [Applying Functional Principles in C# Pluralsight course](https://enterprisecraftsmanship.com/ps-func)## Related Projects
- [Typescript Functional Extensions](https://github.com/seangwright/typescript-functional-extensions)
- [UniTask extensions for Unity](https://github.com/Razenpok/CSharpFunctionalExtensions.UniTask)## Contributors
A big thanks to the project contributors!
- [Marcin Jahn](https://github.com/marcinjahn)
- [Jannes Kaspar-Müller](https://github.com/JKamue)
- [dbuckin1](https://github.com/dbuckin1)
- [bothzoli](https://github.com/bothzoli)
- [Pavel Zemlianikin](https://github.com/PNZeml)
- [Simon Lang](https://github.com/redx177)
- [Nils Vreman](https://github.com/NilsVreman)
- [Scheichsbeutel](https://github.com/Scheichsbeutel)
- [Alexey Malinin](https://github.com/TechnoBerry)
- [Robert Larkins](https://github.com/robertlarkins)
- [tinytownsoftware](https://github.com/tinytownsoftware)
- [piotr121993](https://github.com/piotr121993)
- [Dmitry Korotin](https://github.com/teheran)
- [michalsznajder](https://github.com/michalsznajder)
- [Xavier](https://github.com/xavierjohn)
- [Julien Aspirot](https://github.com/julienasp)
- [Kyle McMaster](https://github.com/KyleMcMaster)
- [Vinícius Beloni Cubas](https://github.com/vinibeloni)
- [rutkowskit](https://github.com/rutkowskit)
- [Giovanni Costagliola](https://github.com/MrBogomips)
- [Mark Wainwright](https://github.com/wainwrightmark)
- [ProphetLamb](https://github.com/ProphetLamb)
- [Paul Williams](https://github.com/Paul-Williams)
- [alexmurari](https://github.com/alexmurari)
- [ruud](https://github.com/ruudhe)
- [Tomasz Malinowski](https://github.com/Yaevh)
- [Staffan Wingren](https://github.com/staffanwingren)
- [Tim Schneider](https://github.com/DerStimmler)
- [Piotr Karasiński](https://github.com/Caleb9)
- [Marcel Roozekrans](https://github.com/MarcelRoozekrans)
- [guythetechie](https://github.com/guythetechie)
- [Logan Kahler](https://github.com/lqkahler)
- [Ali Khalili](https://github.com/AliKhalili)
- [Andrei Andreev](https://github.com/Razenpok)
- [YudApps](https://github.com/YudApps)
- [dataphysix](https://github.com/dataphysix)
- [Laszlo Lueck](https://github.com/LaszloLueck)
- [Sean G. Wright](https://github.com/seangwright)
- [Samuel Viesselman](https://github.com/SamuelViesselman)
- [Stian Kroknes](https://github.com/stiankroknes)
- [dataneo](https://github.com/dataneodev)
- [michaeldileo](https://github.com/michaeldileo)
- [Renato Ramos Nascimento](https://github.com/renato04)
- [Patrick Drechsler](https://github.com/draptik)
- [Vadim Mingazhev](https://github.com/mingazhev)
- [Darick Carpenter](https://github.com/darickc)
- [Stéphane Mitermite](https://github.com/kakone)
- [Markus Nißl](https://github.com/mnissl)
- [Adrian Frielinghaus](https://github.com/freever)
- [svroonland](https://github.com/svroonland)
- [JvSSD](https://github.com/JvSSD)
- [Vladimir Makaev](https://github.com/VladimirMakaev)
- [Ben Smith](https://github.com/benprime)
- [pedromtcosta](https://github.com/pedromtcosta)
- [Michał Bator](https://github.com/MikelThief)
- [mukmyash](https://github.com/mukmyash)
- [azm102](https://github.com/azm102)
- [ThomasDC](https://github.com/thomasdc)
- [bopazyn](https://github.com/bopazyn)
- [Joris Goovaerts](https://github.com/CommCody)
- [Ivan Deev](https://github.com/BillyFromAHill)
- [Damian Płaza](https://github.com/dpraimeyuu)
- [ergwun](https://github.com/ergwun)
- [Michael DiLeo](https://github.com/pilotMike)
- [Jean-Claude](https://github.com/jcsonder)
- [Matt Jenkins](https://github.com/space-alien)
- [Michael Altmann](https://github.com/altmann)
- [Steven Giesel](https://github.com/linkdotnet)
- [Anton Hryshchanka](https://github.com/ahryshchanka)
- [Mikhail Bashurov](https://github.com/saitonakamura)
- [kostekk88](https://github.com/kostekk88)
- [Carl Abrahams](https://github.com/CarlHA)
- [golavr](https://github.com/golavr)
- [Sviataslau Hankovich](https://github.com/hankovich)
- [Chad Gilbert](https://github.com/freakingawesome)
- [Robert Sęk](https://github.com/robosek)
- [Sergey Solomentsev](https://github.com/SergAtGitHub)
- [Malcolm J Harwood](https://github.com/mjharwood)
- [Dragan Stepanovic](https://github.com/dragan-stepanovic)
- [Ivan Novikov](https://github.com/jonny-novikov)
- [Denis Molokanov](https://github.com/dmolokanov)
- [Gerald Wiltse](https://github.com/solvingJ)
- [yakimovim](https://github.com/yakimovim)
- [Alex Erygin](https://github.com/alex-erygin)
- [Omar Aloraini](https://github.com/omaraloraini)