digitecgalaxus / galaxus.functional Goto Github PK
View Code? Open in Web Editor NEWA package bringing popular functional abstractions (e.g. Option or Result) to C#.
License: Other
A package bringing popular functional abstractions (e.g. Option or Result) to C#.
License: Other
Even though I'm not a huge fan of those, there's been a case now of Galaxus.Functional
being used in code with a synchronization context. Lo and behold - Blazor.
To not be the cause of unneccessary slowdown, all the await
s should be accompanied by a ConfigureAwait(false)
.
Building the library procudes a lot of Warnings for the XML Documentation.
There are some LINQ methods like SingleOrDefault
or FirstOrDefault
which just return null
if nothing matches. We should leverage the benefit of option to these extensions, providing SingleOrNone
and similar extensions returning an Option<T>
.
Right now, the package is mostly focused on Option, Result and Either. It'd be nice to include some miscellaneous functions to ease working with lambdas everywhere...
Func<T2, T1, T3> Flip<T1, T2, T3>(Func<T1, T2, T3>)
Func<T, Unit> Do<T>(Action<T>)
T Identity(T)
Func<T> Const<T>(T)
Func<T1, T3> Compose<T1, T2, T3>(Func<T1, T2>, Func<T2, T3>)
Func<T2, T3> Bind<T1, T2, T3>(Func<T1, T2, T3>, T1)
And because of the beauty that is functional programming, the types should be enough to explain what the functions should do.
It would be nice, if one could map an Option<TSome>
to an Either<TSome, TNone>
using a fallback of type TNone
if the option contains None
, without the need to specify the generic types explicitely or to use a Identity lambda function, since both are boilerplate.
I propose a new extension method like this:
public static Either<TSome, TNone> ToEither<TSome, TNone>(this Option<TSome> option, TNone fallback)
=> option.MapOr<Either<TSome, TNone>>(some => some, fallback);
It's kinda odd and I've never seen in before
Would love to have support for MapOrElse on Option to simplify
option.Map(v => new Obj(v)).UnwrapOrElse(() => new Obj(fallback))
When chaining Result
's, there is often the need to work with the results value more than once, e.g. for logging and inform some listeners. One can use the functions Map
and AndThen
to do that, however, we than have to return the input again, which often requires to use heavier code, while with a Tap
often a simple method group could be used.
With Tap one could write
result
.Tap(Console.Write)
.Tap(Publish);
Without the same functionality looks something like this:
result.
.Map(ok =>
{
Console.Write(ok);
return ok;
}).Map(Publish);
The proposed naming is inspired by tap from rxjs and can be argued because of it's simmilarity to map.
As for Map
and AndThen
also a TapAsync
as well as TapErr
and TapErrAsync
should be introduced.
You do a ToList()
in each of them, and lose the Lazyness of the Linq extensions which results in unwanted materialization of the whole IEnumerable<T>
FirstOrNone
should only iterate to the first element, instead it materializes the whole list potentially thousands of elements. The same happens obviously in SingleOrNone
, while it less of a problem in LastOrNone
it is still not functional.
One of the cooler features in newer C# versions is pattern matching, especially something akin to
var foo = bar switch { <0: "Low", 0: "Zero", >0: "High" };
And one of the cooler features in Rust is being able to do this kind of pattern matching on Option
and Result
. So how cool'd it be to mimic this? Basically, being able to do the following:
var foo = bar switch { None: "Empty", Some x: $"Value is {x.Value}" };
Sadly, the compiler will probably warn about the patterns not being exhaustive since there's no catch-all, but maybe there's a way around that.
To do this, though, there'd need to be distinct types for the possibilities - basically imitating a discriminated union.
public abstract class Option<T>
{
// Omitted
}
public sealed class Some<T> : Option<T>
{
// Omitted
}
public sealed class None<T> : Option<T>
{
// Omitted
}
The abstract base would then define all required operations (chaining, mapping, unwrapping) while the derived classes would just contain the very simple implementations, since there's no need to figure out if the current Option
is a Some
or a None
.
This should easily be doable for Option
and Result
. For Either<A, B>
and Either<A, B, C>
, this would take some more effort.. maybe having a class A
(please don't) implementing both Either<A, B>
and Either<A, B, C>
would be a possibility?
Option
does not have a MatchAsync
method. We could add a MatchAsync
method, so that it could accept asynchronous delegates as parameters.
Would love to have async support for MapOr and Or on Option.
For async Pipelines sadly an unnecessary MapOr is needed if the fallback is async:
await option.MapOr(v => Task.FromResult(v), fallback)
The nullable reference types are a cool feature - the few places where do allow nulls would be a great place to put a shiny little ?
.
Right now, packing involves invoking dotnet pack
with the correct path to the .nuspec
files.
I'd love to:
.nuspec
files, having all metadata inside the .csproj
filesIn the end, I want the packing process to be as simple as doing dotnet pack -c Release -o packages/
in the repository root.
Since putting "Oh, this formatting is wrong"-comments everywhere just isn't helpful, we should define a certain codestyle as standard and have a way to "enforce" it.
Either by adding an editorconfig (which is kind of a pain in the arse when supporting multiple IDEs/Editors) or StyleCop analyzers.. or whatever might exist outside of these two.
We would like to have async support for matching either's to avoid blocking wait operations.
Dictionary-like classes offer methods like bool TryGetValue<TKey, TValue>(TKey key, out TValue? value)
or TValue? GetValueOrDefault<TKey, TValue>(TKey)
. To use dictionaries wie Galaxus.Functional it would be nice to have something like:
public static Option<TSome> GetValueOrNone<TKey, TSome>(this IReadOnlyDictionary<TKey, TSome> dictionary, TKey key)
With Option<T>.ToEither(B)
there is a way to convert an Option to an Either with a fallback value. The drawback is, that the Option's value will always be in the A spot and the fallback value in the B spot.
The order of the A, B, (and C) in an Either have no intrinsic semantic meaning. Therefore there exist legitimate cases, where you have an interface, requiring a parameter of type Either<X,Y>
that you would like to call from an Option<Y>
or an Either<Y,X>
.
To achieve this, there are multiple solutions:
Swap()
on Either<TA, TB>
returning an Either<TB, TA>
Option<X>
: AsA(B valueIfNone)
returning an Either<X, B>
and AsB(A valueIfNone)
returning an Either<A, X>
The type constructor of None currently is very verbose. You always have to give the type information of the inner type. This is not necessary in the some case, the type can be inferred from the value.
Instead of
var option = Option<TSource>.Some(value);
It should look like this:
var option = Option.Some(value);
It wouhl be nice to get all As and all Bs of a collection of Eithers
An AttemptToUnwrapNoneWhenOptionContainedSomeException
is thrown, if one tries to unwrap a some value from an Option containing none. The name of the Exception is missleading
Right now, there's a lot of extensions for Result, Option and Either to handle async. MatchAsync
, UnwrapAsync
and all that.. and it kind of feels like noise in the midst of other extension that actually provide new functionality.
I think we could generate these extensions with a source generator, but I'd like to keep that distinctly apart from the rest of the library. So - I'd like to have all async functionaliry extrated to a different project Galaxus.Functional.Async
.
It would still be contained in the same solution - and would still be packaged. The nuget package would simply contain both DLLs, with one being mostly generated code I guess.
Create an additional Nuget that lets you write Tests like
Assert.That(result, Is.Result.InState.Ok.WithValue.GreaterThan(3))
or
Assert.That(option, Is.OptionOfSome.WithValue.EqualTo(2))
This new constraints should also have meaningfull messages if an Assert fails
Would love to have FlatMap option on Result.
Mapping with same error type packing them together.
In bigger Pipelines where the Result on a Map returns a Result itself makes it hard to chain together, especially if you are in an async context.
Result<Foo, Error> CreateFoo(Bar b);
Result<Bar, Error> CreateBar();
CreateBar().Map(b => CreateFoo(b)) // creates Result<Result<Foo, Error>, Error>
CreateBar().FlatMap(b => CreateFoo(b)) // Result<Foo, Error>
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.