Funcky 3.8.0
dotnet add package Funcky --version 3.8.0
NuGet\Install-Package Funcky -Version 3.8.0
<PackageReference Include="Funcky" Version="3.8.0" />
<PackageVersion Include="Funcky" Version="3.8.0" />
<PackageReference Include="Funcky" />
paket add Funcky --version 3.8.0
#r "nuget: Funcky, 3.8.0"
#:package Funcky@3.8.0
#addin nuget:?package=Funcky&version=3.8.0
#tool nuget:?package=Funcky&version=3.8.0
Funcky
Funcky is a functional library for C# which encourages correct usage of the functional programming paradigm.
Documentation
The Funcky book is the place to start. It explains the mindset behind the library,
walks through every monad and every IEnumerable extension with examples and marble diagrams, documents all analyzer
rules, and ends with a few case studies. The changelog lists what changed in each release and the
support policy which target frameworks are supported.
Packages
Core
The correctness analyzers are built into Funcky itself, Funcky.Analyzers adds the opt-in style rules. See the analyzer rules for the full list.
On .NET 10 and newer the async support is fully integrated into Funcky. There Funcky.Async only forwards its types to Funcky, so a project can drop the reference once it targets .NET 10.
Testing
Interoperability
These packages live in their own repositories.
| Package | NuGet | Description |
|---|---|---|
| Funcky.EntityFrameworkCore | Interoperability between Funcky and EF Core. | |
| Funcky.DiscriminatedUnion | Source generator for Match methods on discriminated unions. Retiring, see below. |
Funcky.DiscriminatedUnion is retiring with Funcky 4.0.0. C# gets native union types and closed hierarchies
with .NET 11, which Funcky 4.0.0 adopts. The generator keeps working with the 3.x line but receives no new features,
see #900.
Highlights
Option: no Value, no HasValue
The Option<T> monad is the centerpiece of Funcky. It represents a value that may be absent, and it deliberately has
no Value, HasValue, IsSome or GetOrThrow. Instead of checking and extracting, you tell the option what to do
with the value, using the names LINQ already taught you: Select, SelectMany, Where.
Option<string> input = ...;
var result = input
.SelectMany(v => v.ParseInt32OrNone())
.Where(n => n >= 0)
.Select(n => $"Non-negative: {n}");
result.Switch(
none: () => Console.WriteLine("nothing to show"),
some: Console.WriteLine);
The way out at the edges of your program is to say what happens in both cases, with Match, Switch or GetOrElse.
The Option chapter is organized as a ladder of answers to
"how do I get the value out?".
The TryVerb pattern: …OrNone instead of Try… with out
TryParse, TryGetValue, FirstOrDefault, Max: Funcky gives every BCL member that may have no answer an …OrNone
counterpart that returns an Option instead of a default value or an out parameter. The result composes, and the
compiler no longer lets you forget the missing case.
var port = settings.GetValueOrNone("port")
.SelectMany(value => value.ParseInt32OrNone())
.Where(p => p is > 0 and < 65536)
.GetOrElse(8080);
var oldest = people.MaxByOrNone(person => person.Age);
Result and Either for computations that can fail
Result<T> carries either a value or an Exception, Either<L, R> carries one of two arbitrary types. Both support
Select and SelectMany like Option does, so a chain of fallible steps reads like a query and the failure is handled once,
at the end.
Result<Order> order = ParseOrder(json)
.SelectMany(Validate)
.SelectMany(Price);
return order.Match(
ok: o => Results.Ok(o),
error: e => Results.BadRequest(e.Message));
IEnumerable extensions
Funcky adds the sequence operators LINQ lacks: windowing, pairing, interspersing, partitioning, side effects, and
…OrNone variants of every element query. They are lazy where LINQ is lazy and work together with the monads above.
Sequence.Return(1, 2, 3, 4)
.Pairwise((left, right) => left + right) // [3, 5, 7]
.Intersperse(-1) // [3, -1, 5, -1, 7]
.Inspect(Console.WriteLine)
.SlidingWindow(3) // [[3, -1, 5], [-1, 5, -1], [5, -1, 7]]
.WhereSelect(window => window.AverageOrNone()) // [2, 1, 3]
.JoinToString(", "); // "2, 1, 3"
Analyzers that catch misuse
Funcky ships with built-in analyzers that turn misuse of its types into compile errors, for example instantiating
a Result or Either with default or treating an option as a collection of more than one element. The optional
Funcky.Analyzers package adds style rules with code fixes that lead from the general tool to the specific one, for
example from option.Match(none: fallback, some: Identity) to option.GetOrElse(fallback).
… and more
ReaderandLazymonads, andUnitas a first-class "no value" type.- Functional helpers:
Identity,Compose,Curry,Flip, predicate composition, andRetrywith configurable retry policies. - «Constructors» for
IEnumerable:Sequence.Return,Sequence.Successors,Sequence.Cycle, etc. IAsyncEnumerablecounterparts of the sequence extensions, andawaitsupport forOption<Task<T>>.- Interoperability with
IQueryable,string,Stream,Nullable<T>and the numeric types. - Supports every framework from .NET Standard 2.0 and .NET Framework 4.8 to the newest .NET, and is trimming and Native AOT compatible.
Motivation
Functional programming is the oldest of the three major programming paradigms, nonetheless it is the last to get widespread usage. Even in languages like C++, Java or C# we want to use a functional style of programming.
LINQ is the first monad which got widespread use in C#, and most C# programmers were not even aware of it being a monad, which probably helped.
Mark Seemann points out in How to get the value out of the monad:
Unfortunately, Maybe implementations often come with an API that enables you to ask a Maybe object if it's populated or empty, and a way to extract the value from the Maybe container. This misleads many programmers [...]
This library is based on his example code and grows slowly into a library which helps to use and understand the functional programming paradigm. Functional programming is side-effect free, and the strong type system can be used to make illegal state impossible.
Use functional programming as an additional asset to write correct code.
Other libraries
There are several libraries available which try to give you more functional features in C#. In comparison:
- Funcky wants to be functional C#. It builds on LINQ, lambdas and the type system you already use.
- Funcky uses the C# monadic interfaces as an advantage. Query syntax and
awaitwork with its monads. - Funcky does not provide its own record or union types. Use the record types built into C#.
LanguageExt
This library is probably the most complete attempt at functional programming in C#, however it is very opinionated and admits to being not very idiomatic in C#. It certainly is more mature than Funcky and has a lot of features. If you want to go fully functional and for some reason cannot use F#, this might be the way to go.
Eff
Eff is inspired by the Eff programming language and the implementation of algebraic effects. Its only purpose is the
handling of side effects, using the await syntax in a very elegant way.
We think the approach is very nice but cumbersome in usage, however we really love the approach with the await syntax.
The library is very specialised and can be used in combination with any other functional style library.
MoreLinq
MoreLinq provides more extension functions on IEnumerable, but has no additional functional concepts. We also provide
additional extension functions on IEnumerable, but we also try to make them work in combination with our monads and
the async monad. The different monad syntaxes in C# (LINQ, async) do not play nicely together.
… and more
License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-Apache or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Contribution
Contributions are more than welcome. Just open a PR 😃 If you want something easy to work on, there are a few issues marked with good first issue.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
Documentation
To build the documentation you need mdBook installed.
When working on the documentation it's useful to have mdbook watching and automatically rebuilding on changes:
mdbook serve Documentation
Dependency Policy
The core Funcky package is not allowed to have dependencies. Backwards compatibility packages from Microsoft that are included in
newer framework versions (e.g. Microsoft.Bcl.AsyncInterfaces, System.Collections.Immutable) are exempt from this rule.
Interoperability with other libraries is provided in separate packages (e.g. Funcky.Xunit, Funcky.EntityFrameworkCore).
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 is compatible. net5.0-windows was computed. net6.0 is compatible. 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 is compatible. 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 is compatible. 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. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 is compatible. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 is compatible. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETCoreApp 3.1
- System.Text.Json (>= 5.0.2)
-
.NETStandard 2.0
- Microsoft.Bcl.HashCode (>= 1.1.1)
- System.Collections.Immutable (>= 1.7.1)
- System.Text.Json (>= 5.0.2)
-
.NETStandard 2.1
- System.Collections.Immutable (>= 1.7.1)
- System.Text.Json (>= 5.0.2)
-
net10.0
- No dependencies.
-
net5.0
- No dependencies.
-
net6.0
- No dependencies.
-
net7.0
- No dependencies.
-
net8.0
- No dependencies.
-
net9.0
- No dependencies.
NuGet packages (15)
Showing the top 5 NuGet packages that depend on Funcky:
| Package | Downloads |
|---|---|
|
Messerli.LinqToRest
A LINQ QueryProvider that uses a REST API as its data source. |
|
|
Funcky.Xunit
Package to use Funcky with xUnit |
|
|
Messerli.ChangeCase
Transform a string between different casings. |
|
|
Funcky.EntityFrameworkCore
Interoperability between Funcky and EF Core |
|
|
Funcky.Async
Extends Funcky with support for IAsyncEnumerable and Tasks on frameworks older than .NET 10. On .NET 10 and newer these APIs are part of the Funcky package and this package only forwards to them. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 3.8.0 | 120 | 10/29/2026 |
| 3.7.0 | 138 | 10/20/2026 |
| 3.6.1 | 211 | 10/16/2026 |
| 3.6.0 | 5,141 | 2/12/2026 |
| 3.5.1 | 7,532 | 2/4/2025 |
| 3.5.0 | 6,146 | 1/15/2025 |
| 3.4.0 | 4,413 | 4/30/2024 |
| 3.3.0 | 9,070 | 7/14/2023 |
| 3.2.0 | 2,722 | 5/17/2023 |
| 3.1.0 | 7,598 | 11/16/2022 |
| 3.0.0 | 13,130 | 9/14/2022 |
| 2.7.1 | 947 | 6/13/2022 |
| 2.7.0 | 3,753 | 4/20/2022 |
| 2.6.0 | 1,258 | 12/16/2021 |
| 2.5.0 | 2,574 | 6/29/2021 |
| 2.4.1 | 761 | 5/21/2021 |
| 2.4.0 | 2,612 | 2/25/2021 |
| 2.3.0 | 1,521 | 11/27/2020 |
| 2.3.0-beta.1 | 460 | 10/22/2020 |
| 2.2.0 | 2,819 | 8/24/2020 |