DeFuncto.Core
1.0.4
See the version list below for details.
dotnet add package DeFuncto.Core --version 1.0.4
NuGet\Install-Package DeFuncto.Core -Version 1.0.4
<PackageReference Include="DeFuncto.Core" Version="1.0.4" />
paket add DeFuncto.Core --version 1.0.4
#r "nuget: DeFuncto.Core, 1.0.4"
// Install DeFuncto.Core as a Cake Addin #addin nuget:?package=DeFuncto.Core&version=1.0.4 // Install DeFuncto.Core as a Cake Tool #tool nuget:?package=DeFuncto.Core&version=1.0.4
DeFuncto
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.
Functional Programming as a tool: Making your little dev world safer
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:
- Error handling
- Tasks
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.
Documentation
Product | Versions 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 | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
.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. |
-
.NETStandard 2.0
- No dependencies.
NuGet packages (1)
Showing the top 1 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. |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
1.1.0 | 12 | 11/5/2024 |
1.0.7 | 1,934 | 12/7/2023 |
1.0.6 | 137 | 12/7/2023 |
1.0.5 | 167 | 12/7/2023 |
1.0.4 | 1,100 | 1/4/2023 |
1.0.3 | 1,186 | 6/16/2022 |
1.0.2 | 710 | 5/14/2022 |
1.0.1 | 653 | 5/14/2022 |
1.0.0 | 698 | 5/14/2022 |
0.3.1 | 730 | 3/11/2022 |
0.3.0 | 1,353 | 2/9/2022 |
0.2.15 | 517 | 12/31/2021 |
0.2.14 | 398 | 12/24/2021 |
0.2.13 | 379 | 12/16/2021 |
0.2.12 | 702 | 11/5/2021 |
0.2.11 | 504 | 10/26/2021 |
0.2.10 | 489 | 9/23/2021 |
0.2.9 | 474 | 9/23/2021 |
0.2.8 | 516 | 9/20/2021 |
0.2.7 | 499 | 7/28/2021 |
0.2.6 | 481 | 7/22/2021 |
0.2.5 | 479 | 7/16/2021 |
0.2.4 | 696 | 7/16/2021 |
0.2.3 | 524 | 7/13/2021 |
0.2.2 | 559 | 7/9/2021 |
0.2.1 | 336 | 7/8/2021 |
0.2.0 | 365 | 7/8/2021 |
0.1.4 | 377 | 7/8/2021 |
0.1.3 | 324 | 7/8/2021 |
0.1.2 | 363 | 7/6/2021 |
0.1.1 | 305 | 7/6/2021 |
0.1.0 | 313 | 7/5/2021 |