Giter VIP home page Giter VIP logo

csharp.made.functional's Introduction

CSharp.Made.Functional

codecov

Add CSharp.Made.Functional

dotnet add package CSharp.Made.Functional

Using statements

The namespace does not match the package name to keep using statements shorter.

// Base using statement:
global using Functional;

// To use Option types:
global using Functional.Options;

// To use Result types:
global using Functional.Results;

// To use Union types:
global using Functional.Unions;

// For extension methods like Pipe/Tap:
global using Functional.Common;

// For exception handling extensions:
global using static Functional.Exceptions.ExceptionExtensions;

// For static methods like Cons:
global using static Functional.Common.CommonExtensions;

// Other usings for other static methods:
global using static Functional.Options.OptionExtensions;
global using static Functional.Results.ResultExtensions;

For examples and more discussion about this library, Read the Docs!

csharp.made.functional's People

Contributors

th3oth3rjak3 avatar

Stargazers

 avatar

Watchers

 avatar

csharp.made.functional's Issues

Try/catch isn't very ergonomic.

The try/catch methods aren't great and lead to a lot of weirdness. The "Try" method shouldn't return a "TryResult", instead it should just always return a Result<T, Exception>. In some cases this could mean that T is already a result. In that case a return type could be Result<Result<A, B>, Exception>. This would be the easiest way to handle this. Then Match, MatchAsync and other methods would already work.

Effect doesn't allow Action and Action<T> mixed.

When using Result.Effect, it doesn't allow a user to enter an Action with no input for the success case and an Action for the error case.

Example:

Try(() => 
    somethingThatCouldFail())
    .Effect(
        _ => Assert.Fail(), // This should be able to be () => Assert.Fail() but is not provided by the library.
        error => Assert.IsInstanceOfType<Exception>(error));

TapOk, TapError, TapSome, and TapNone would be useful.

Sometimes we only want to do an action when a value is some, none, ok, or error depending on the type.

Example:

// Current approach
        Result.Ok("it's okay")
            .Tap(result =>
                result.Match(
                    ok => Unit.Default.Tap(_ => Console.WriteLine(ok.ToString())),
                    err => Unit.Default));

// New approach
        Result.Ok("it's okay")
            .TapOk(ok => Console.WriteLine(ok))
            .ShouldBeOfType<Result<string, Exception>>();
)
  • Result.Tap
  • Result.TapOk => Action<T>
  • Result.TapOk => Action
  • Result.TapError => Action<T>
  • Result.TapError => Action
  • Result.TapAsync => Action<T>, Action<T>
  • Result.TapAsync => Action<T>, Action
  • Result.TapAsync => Action<T>, Func<Task>
  • Result.TapAsync => Action<T>, Func<T, Task>
  • Result.TapAsync => Action, Action<T>
  • Result.TapAsync => Action, Action
  • Result.TapAsync => Action, Func<Task>
  • Result.TapAsync => Action, Func<T, Task>
  • Result.TapAsync => Func<Task>, Action<T>
  • Result.TapAsync => Func<Task>, Action
  • Result.TapAsync => Func<Task>, Func<Task>
  • Result.TapAsync => Func<Task>, Func<T, Task>
  • Result.TapAsync => Func<T, Task>, Action<T>
  • Result.TapAsync => Func<T, Task>, Action
  • Result.TapAsync => Func<T, Task>, Func<Task>
  • Result.TapAsync => Func<T, Task>, Func<T, Task>
  • Result.TapOkAsync => Action<T>
  • Result.TapOkAsync => Action
  • Result.TapOkAsync => Func<Task>
  • Result.TapOkAsync => Func<T, Task>
  • Result.TapErrorAsync => Action<T>
  • Result.TapErrorAsync => Action
  • Result.TapErrorAsync => Func<Task>
  • Result.TapErrorAsync => Func<T, Task>
  • Option.Tap => Action<T>, Action
  • Option.Tap => Action, Action
  • Option.TapSome => Action<T>
  • Option.TapSome => Action
  • Option.TapNone => Action
  • Option.TapAsync => Action<T>, Action
  • Option.TapAsync => Action<T>, Func<Task>
  • Option.TapAsync => Action, Action
  • Option.TapAsync => Action, Func<Task>
  • Option.TapAsync => Func<T, Task>, Action
  • Option.TapAsync => Func<T, Task>, Func<Task>
  • Option.TapAsync => Func<Task>, Action
  • Option.TapAsync => Func<Task>, Func<Task>
  • Option.TapSomeAsync => Action<T>
  • Option.TapSomeAsync => Action
  • Option.TapSomeAsync => Func<T, Task>
  • Option.TapSomeAsync => Func<Task>
  • Option.TapNoneAsync => Action
  • Option.TapNoneAsync => Func<Task>

EffectAsync needs more methods for Option

    public static Task<Unit> EffectAsync<T>(this Task<Maybe<T>> maybe, Action<T> whenSome, Func<Task> whenNone) => throw new NotImplementedException();
    public static Task<Unit> EffectAsync<T>(this Task<Maybe<T>> maybe, Action whenSome, Func<Task> whenNone) => throw new NotImplementedException();
    public static Task<Unit> EffectAsync<T>(this Task<Maybe<T>> maybe, Func<T, Task> whenSome, Action whenNone) => throw new NotImplementedException();
    public static Task<Unit> EffectAsync<T>(this Task<Maybe<T>> maybe, Func<T, Task> whenSome, Func<Task> whenNone) => throw new NotImplementedException();
    public static Task<Unit> EffectAsync<T>(this Task<Maybe<T>> maybe, Func<Task> whenSome, Action whenNone) => throw new NotImplementedException();
    public static Task<Unit> EffectAsync<T>(this Task<Maybe<T>> maybe, Func<Task> whenSome, Func<Task> whenNone) => throw new NotImplementedException();

Result.Error should take a string to create a new exception if one isn't provided.

Result.Error requires a new Exception be generated to pass a message. This is fine for most cases because you may want to create an ArgumentNullException or some other more specific exception. However, sometimes you want to just pass an error message and the exception type is irrelevant.

// Current
var error = Result.Error<string>(new Exception("error message")); // verbose

// Proposed
var error = Result.Error<string>("error message"); // Constructs a new Exception behind the scenes.

Something in MapAsync or ReduceAsync isn't awaiting tasks.

Need to rewrite and evaluate these methods to ensure they await all tasks properly. It seems like the best course of action is to write the internals of this library in a more imperative style.

The error occurred when the map and reduce functions both took functions which returned non-task values.

Result.Bind cannot return a different Error type.

Because Bind does not know how to convert one error type to another, the current implementation does not allow binding to a new error type. Example:

public static Result<string, OtherCustomError> SomeBindingOperation(int number) =>
    Result.Failure<string, OtherCustomError>(new OtherCustomError("How do we bind this?"));

Result.Error<int, CustomError>(new CustomError("something bad happened"))
    .Bind(SomeBindingOperation);

The binding operation will fail because it can't figure out how to convert OtherCustomError to CustomError.

More research required.

There is no unit type in the library

Sometimes we need to return a Result<Unit, Exception>. This helps us know if something succeeded or failed, but we didn't need the return value.

Create a "Unit" struct to handle this.

Async Improvements

  • Async methods need cancellation token support
  • Add a Parallelize enum with Sequential and Parallel options
  • Add parallel implementations to each async method that use sequential as the default.
  • Consider adding static methods like Prelude.Sequential and Prelude.Parallel that are internal to handle sequential and parallel processing. These could be used to simplify implementation in other functions throughout the library.

Documentation Needs Improvement

There needs to be better documentation showing examples of how to use various parts of the library. Either example projects showing usage, or documentation showing usage would be helpful.

  • Option
  • Union
  • Result
  • Monadic
  • Exceptions

Improve Documentation

After the recent updates, we need to add documentation for the following items:

  • Result.TapOk
  • Result.TapError
  • Option.TapSome
  • Option.TapNone
  • Effect and EffectAsync static methods

EffectSomeAsync complains that Func<T, Task> is not awaited

Task thing(value) => Task.FromResult(value);
Option.Some("value").AsAsync().EffectSomeAsync(value => thing(value)); // gets green underline saying the result isn't awaited.
Option.Some("value").AsAsync().EffectSomeAsync(async value => await thing(value)); // this is okay though.

There is no "MapError" method for Result type.

Example:

var result = 
    Result.Ok<int, CustomError>(100)
        .Map(number => number.ToString());

result is converted from Result<int, CustomError> to Result<string, CustomError>, but what if we want to map CustomError to NewCustomError?

Library might benefit from IsSome and IsNone methods.

In some instances, an IsSome and IsNone method might be useful. For example, in a Blazor webassembly app, when choosing whether to render content or not. See example below:

Less than ideal readability.

@if(MyReadDto.Match(_ => true, () => false) {
    <RenderThisWhenSome />
}

@code {
    [Parameter]
    public Option<SomeReadDto> MyReadDto { get; set; ] = Option.None<SomeReadDto>();
}

Improved Readability

@if(MyReadDto.IsSome) {
    <RenderThisWhenSome />
}

@code {
    [Parameter]
    public Option<SomeReadDto> MyReadDto { get; set; ] = Option.None<SomeReadDto>();
}

Optional method does not work well with structs.

When a DateTime is nullable, the optional extension doesn't convert DateTime? to DateTime correctly. This probably has something to do with Nullable.

Example:

DateTime? thing = null;
thing
    .Optional()
    .Map(dt => dt.ToString("dd-MMM-yyyy")) // this still thinks that the DateTime is possibly null.
    .Reduce(() => string.Empty);

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.