Giter Club home page Giter Club logo

galaxus.functional's Introduction

Galaxus.Functional

Introduction

Galaxus.Functional is a package that aims to bring FP-style features to C#. Note though, that this is not true functional programming; this package contains some useful abstractions often found in functional languages.

The style is greatly inspired by the way Rust does this. If you have worked with Rust's std::option or std::result, you will probably feel right at home.

Abstractions

There currently are the following abstractions found inside Galaxus.Functional:

Option

A type used to explicitly mark the presence or absence of a value. This is an alternative to checking for null and is similar to e.g. Haskell's Maybe or Java's Optional.

Unit

A type for something that does not exist, similar to void - except that it can be returned and stored as a value.

Either

A type that can be one of several variants, sort of like a discriminated union or an enum with a stored value for each enum member.

Result

A type that explicitly propagates success or error to the caller, similar to used explicitly annotated exceptions. It can be seen as a specialization of an Either with the second variant being an error.

Contribute

No matter how small, we value every contribution! If you wish to contribute,

  1. Please create an issue first - this way, we can discuss the feature and flesh out the nitty-gritty details
  2. Fork the repository, implement the feature and submit a pull request
  3. Once the maintainers have released a new version, your feature will be part of the NuGet package

And always remember the golden rule for every contribution: Be excellent to each other!

galaxus.functional's People

Contributors

adrianwaeber avatar alminsch avatar b4n4n4j03 avatar boskicthebrain avatar flipbug avatar freeapophis avatar nightruby-isa avatar noelwidmer avatar phkiener avatar raphaelimahorn avatar syrotkin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

galaxus.functional's Issues

Extract async-extensions into their own "module"

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.

ConfigureAwait(false) for async extensions

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 awaits should be accompanied by a ConfigureAwait(false).

Allow pattern-matching on Option, Result and maybe even Either

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?

Enforce (?) code style

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.

Option to Either

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);

Add NUnit Testing Extensions

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

Implementation of FirstOrNone, LastOrNone and SingleOrNone are not Lazy.

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.

Polish package packing process

Right now, packing involves invoking dotnet pack with the correct path to the .nuspec files.

I'd love to:

  • Get rid of the .nuspec files, having all metadata inside the .csproj files
  • Having SourceLink set up for the packages
  • Making the build deterministic

In the end, I want the packing process to be as simple as doing dotnet pack -c Release -o packages/ in the repository root.

Introduce Tap

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.

Add async Option.MapOr and Or

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)

Helper methods and method helpers

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.

LINQ-Extensions using Option<T>

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>.

Add Option.MapOrElse

Would love to have support for MapOrElse on Option to simplify
option.Map(v => new Obj(v)).UnwrapOrElse(() => new Obj(fallback))

Option-Type value getter for dictionaries

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)

Nullable reference types

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 ?.

Add a way to convert an Option<T> to an Either<A, T>

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:

  1. Introduce a method e.g. Swap() on Either<TA, TB> returning an Either<TB, TA>
  2. Introduce two methods on an Option<X>: AsA(B valueIfNone) returning an Either<X, B> and AsB(A valueIfNone) returning an Either<A, X>

Add Result.FlatMap

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>

Consider an Option.Some constructor relying on type inference.

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);

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.