![]() |
VOOZH | about |
dotnet add package DeFuncto.Core --version 1.1.3
NuGet\Install-Package DeFuncto.Core -Version 1.1.3
<PackageReference Include="DeFuncto.Core" Version="1.1.3" />
<PackageVersion Include="DeFuncto.Core" Version="1.1.3" />Directory.Packages.props
<PackageReference Include="DeFuncto.Core" />Project file
paket add DeFuncto.Core --version 1.1.3
#r "nuget: DeFuncto.Core, 1.1.3"
#:package DeFuncto.Core@1.1.3
#addin nuget:?package=DeFuncto.Core&version=1.1.3Install as a Cake Addin
#tool nuget:?package=DeFuncto.Core&version=1.1.3Install as a Cake Tool
Functional library for C#, aiming to keep the minimum data types for ease of maintenance.
Heavily inspired by language-ext, but keeping it closer to F# most used structures and with a much less ambitious scope, making it easier to keep it up to date with the latest versions of the language.
The approach is to let C# developers to dip a toe in the pool of programming in a safer way. It's meant in a pragmatic and simplistic way and nothing mentioned in this documents should be considered a computer science compendium on how to write mathematically correct software, but a cookbook of recipes to move you closer to that.
Wait, mathematically correct software? Is that even possible?
Indeed, you can write software in a way where invalid states are impossible to represent. Most of the mistakes you can make will just not compile. There's even an operating system microkernel that is proven to be correct to the last line.
So, I can achieve this "correctness" thing with DeFuncto?
No. The data types defined here are structs, which by default have an empty constructor (C# does not allow to remove it) that will, with the notable exception of Option and AsyncOption (where it's done in a bit of a hacky way) screw any hope of correctness.
There IS a way around it, and language-ext does it by implementing the concept of Bottom, commonly represented as _|_, and a term I'll avoid using at all costs in this documentation. Again, this library is not aiming to get you to swim in the deep end, but to have a smooth transition into safer practices. By keeping the implementation to the minimum, we can maximize the time invested in documentation, sharing the motivations and value of the practices promoted here as well as common pitfalls and tricks to maximize the gains.
Does this mean that I should just skip this and go straight away to use F#?
If you want and can, yeah, do. This is for those of us that want to make our C# journey a little bit easier.
The goal is not to make a full introduction to functional programming. When you arrived here I assumed that you already have heard of the word Monad (another word that you can forget right away, I'm not using it again) and/or you might be interested in railway oriented programming. This library aims EXACTLY to give you the tools to do railway oriented programming and nothing else. The idea is that you will end up writing the majority of your programs in a DSL fashion. Defining your work as events that should happen instead of how they happen.
Let's say that your services all return AsyncResult<Something, Error>, this is what a login program would look like (for the hardcore FPers: I am aware that this is using old fashioned dependency-injected services) in your session handler:
public AsyncResult<SessionToken, Error> Login(string name, string password) =>
from user in userService.FindUser(name)
from authenticatedUser in cryptoService.Validate(user, password)
from token in sessionService.TryCreateToken(authenticatedUser)
select token;
What? That's a query, not a method executing logic.
It's not only executing logic, but doing so asynchronously, this chunk of logic is abstracting away:
The first method called could look something like this:
public AsyncResult<User, Error> FindUser(string name)
{
// Some verbosity that you might grow used to, if you don't, you can always make this return
// Task<Result<User, Error>> and invoke .Async() in the caller.
return Go().Async();
async Task<Result<User, Error>> Go()
{
var possibleUser = await db.Users.FirstOrDefaultAsync(u => u.Name == name);
return possibleUser is not null
? Result<User, Error>.Ok(user)
: Result<User, Error>.Error(new Error.EntityNotFound($"User named {name} was not found in the database"));
}
}
Then, the data type takes care of consuming the user in the next method or circuit breaking in case of an error.
But I don't like magic! I want to see my control flow.
This is not magic, if you change the type returned from FindUser this will stop compiling, wether you return something different than User on the Ok case or something that is not an Error on the Error side. The control flow itself happens internally in the data structure just like it happens in the CLI when you do an if else, except that this is a "conditional" that you have to work REALLY hard to type wrong and have it compile.
It's all about having the compiler hold your hand for as long as possible. Want to know more? Dive into the docs.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 net5.0 was computed. net5.0-windows net5.0-windows was computed. net6.0 net6.0 was computed. net6.0-android net6.0-android was computed. net6.0-ios net6.0-ios was computed. net6.0-maccatalyst net6.0-maccatalyst was computed. net6.0-macos net6.0-macos was computed. net6.0-tvos net6.0-tvos was computed. net6.0-windows net6.0-windows was computed. net7.0 net7.0 was computed. net7.0-android net7.0-android was computed. net7.0-ios net7.0-ios was computed. net7.0-maccatalyst net7.0-maccatalyst was computed. net7.0-macos net7.0-macos was computed. net7.0-tvos net7.0-tvos was computed. net7.0-windows net7.0-windows was computed. net8.0 net8.0 was computed. net8.0-android net8.0-android was computed. net8.0-browser net8.0-browser was computed. net8.0-ios net8.0-ios was computed. net8.0-maccatalyst net8.0-maccatalyst was computed. net8.0-macos net8.0-macos was computed. net8.0-tvos net8.0-tvos was computed. net8.0-windows net8.0-windows was computed. net9.0 net9.0 was computed. net9.0-android net9.0-android was computed. net9.0-browser net9.0-browser was computed. net9.0-ios net9.0-ios was computed. net9.0-maccatalyst net9.0-maccatalyst was computed. net9.0-macos net9.0-macos was computed. net9.0-tvos net9.0-tvos was computed. net9.0-windows net9.0-windows was computed. net10.0 net10.0 was computed. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 netcoreapp2.0 was computed. netcoreapp2.1 netcoreapp2.1 was computed. netcoreapp2.2 netcoreapp2.2 was computed. netcoreapp3.0 netcoreapp3.0 was computed. netcoreapp3.1 netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 netstandard2.0 is compatible. netstandard2.1 netstandard2.1 was computed. |
| .NET Framework | net461 net461 was computed. net462 net462 was computed. net463 net463 was computed. net47 net47 was computed. net471 net471 was computed. net472 net472 was computed. net48 net48 was computed. net481 net481 was computed. |
| MonoAndroid | monoandroid monoandroid was computed. |
| MonoMac | monomac monomac was computed. |
| MonoTouch | monotouch monotouch was computed. |
| Tizen | tizen40 tizen40 was computed. tizen60 tizen60 was computed. |
| Xamarin.iOS | xamarinios xamarinios was computed. |
| Xamarin.Mac | xamarinmac xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos xamarinwatchos was computed. |
Showing the top 3 NuGet packages that depend on DeFuncto.Core:
| Package | Downloads |
|---|---|
|
DeFuncto.Assertions
Assertions for DeFuncto data types. Assertions fail by throwing an exceptions, which is acknowledged by virtually every popular .NET testing library as a way to fail a test. Not yet localized, assertions fail messages are always in English. |
|
|
Nvx.ConsistentAPI.TestUtils
Test helpers for the Event Modeling framework Nvx.ConsitentAPI |
|
|
Nvx.ConsistentAPI
Event Modeling framework |
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.1.3 | 1,213 | 9/2/2025 |
| 1.1.2 | 2,728 | 3/16/2025 |
| 1.1.1 | 1,058 | 11/12/2024 |
| 1.1.0 | 335 | 11/5/2024 |
| 1.0.7 | 2,208 | 12/7/2023 |
| 1.0.6 | 334 | 12/7/2023 |
| 1.0.5 | 354 | 12/7/2023 |
| 1.0.4 | 1,484 | 1/4/2023 |
| 1.0.3 | 1,389 | 6/16/2022 |
| 1.0.2 | 935 | 5/14/2022 |
| 1.0.1 | 870 | 5/14/2022 |
| 1.0.0 | 913 | 5/14/2022 |
| 0.3.1 | 942 | 3/11/2022 |
| 0.3.0 | 1,579 | 2/9/2022 |
| 0.2.15 | 737 | 12/31/2021 |
| 0.2.14 | 628 | 12/24/2021 |
| 0.2.13 | 594 | 12/16/2021 |
| 0.2.12 | 932 | 11/5/2021 |
| 0.2.11 | 720 | 10/26/2021 |
| 0.2.10 | 709 | 9/23/2021 |