louthy / language-ext

C# functional language extensions - a base class library for functional programming
MIT License
6.48k stars 417 forks source link

What types should async methods return #1380

Closed slimshader closed 3 days ago

slimshader commented 6 days ago

Hi,

I trying to wrap my head around synchrony with LangExt but I am stuck on the simplest thing: I'd like my services like IFileDownlaoder to return OptionAsync<byte[]> instead of Task<Option<byte[]>> but implantation as simple as

public async OptionAsync<int> GetIntAsync()
{
   await Task.Delay(TimeSpan.FromSeconds(1));
   return 42;
}

fails to compile with error:

The return type of an async method must be void, Task, Task<T>, a task-like type, IAsyncEnumerable<T>, or IAsyncEnumerator<T>

Indeed, when looking upon implementations OptionAsync nor EitherAsync implement custom awaitables and related async machinery.

So: what the return types of async methods should be so that functional pipelines could be built from LangExt types?

louthy commented 6 days ago

If you haven't started using the *Async variants yet, then you may want to skip them and go straight to v5. The proposal to remove the *Async variants has been acted upon and they don't exist any more.

If you want to continue with OptionAsync on v4 then you use LINQ, not async/await:

static OptionAsync<int> GetIntAsync() =>
    from _ in SomeAsync(wait(1))
    select 42;

static async Task<Unit> wait(int seconds)
{
    await Task.Delay(TimeSpan.FromSeconds(1));
    return unit;
}

So, all of the standard functional operators (Map, Bind, Apply, etc.) understand the asynchrony and apply it as part of their implementations.

In version 5 you'd use the IO monad (support for async essentially) with other monad-transformers:

static OptionT<IO, int> GetIntAsync() =>
    from _ in wait(1)
    select 42;

static IO<Unit> wait(int seconds) =>
    liftIO(async e => await Task.Delay(TimeSpan.FromSeconds(1), e.Token));

The benefit of the new approach is: