question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Add Future[A] that helps with async code

See original GitHub issue

I am lately writing a lot of asynchronous code (using asyncio mostly) and while async/await is fairly straight forward and nice to work with, it hinders writing modular code with small functions and reuse of said functions. This is imho due to two issues in particular:

  1. regular functions def foo(... and coroutines async def bar(... have different calling semantics. For a regular function you just do foo(...) to get your result back while for coroutines you need to await the result, i.e. use await bar(...)
  2. coroutines don’t compose well (without pouring await all over your code)

Let’s define a couple simple functions (both regular and coroutines) to illustrate these issues:

def double(x): 
    return 2*x

async def delay(x):
    return x

async def double_async(x):
    return 2*x

Cases:

  1. Composing regular functions just for reference - easy:

    double(double(1))
    
  2. Composing regular and coroutine functions V1 - not so bad,:

    await double_async(double(1))
    
  3. Composing regular and coroutine functions V2 - awkward with the await thrown into the middle:

    double(await double_async(1))
    
  4. Composing two coroutine functions - awaits galore :

    await double_async(await double_async(1))
    

To ease this pain I propose a (monad-like) container that implements map and bind so that functions can be chained nicely

class Promise:
    
    def __init__(self, coro):
        self._coro = coro
        
    def map(self, f):
        async def coro():
            x = await self._coro
            return f(x)
        return Promise(coro())
    
    def bind(self, f):
        async def coro():
            x = await self._coro
            return await f(x)
        return Promise(coro())
    
    def __await__(self):
        return self._coro.__await__()

Usage

  • Wrap a coroutine (function defined with async def) to create an instance of Promise.
    p = Promise(delay(1))
    await p  # get the result of the wrapped coroutine
    
  • Call a regular function on an instance of Promise
    await Promise(delay(1)).map(double)
    
  • Call a coroutine on an instance of Promise
    await Promise(delay(1)).bind(double_async)
    

Extended example

Since the examples above don’t look too intimidating, behold:

await (
    double_async(
        double(
            await double_async(
                await delay(1)
            )
        )
    )
)

vs.:

await (
    Promise(delay(1))
    .bind(double_async)
    .map(double)
    .bind(double_async)
)

Feedback greatly appreciated!

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:3
  • Comments:31 (20 by maintainers)

github_iconTop GitHub Comments

5reactions
stereobuttercommented, Mar 5, 2020

Over the weekend I will probably have some spare time to work on this 😃 I also played around with lists of continuations (I implemented another monad ListContinuation that works over multiple continuations) because running multiple things at the same time is usually what async code is all about. I don’t know what your take is on more specialized monads, monad transfomers and extensible effects for nesting monads inside one another.

3reactions
sobolevncommented, Apr 25, 2020

After playing around asyncio and Future a bit, I would say that a correct way to handle this would be:

  1. Drop all async functions we have
  2. Forbid to use async keyword in wemake-python-styleguide and ban import asyncio
  3. Close this issue
  4. Burn everything with fire! 🔥
Read more comments on GitHub >

github_iconTop Results From Across the Web

Async Programming in Rust — Part 3: Futures and Async / Await
Futures are a way for us to represent values that should exist at some point in the future as the result of an...
Read more >
Asynchronous programming: futures, async, await - Dart
This codelab teaches you how to write asynchronous code using futures and the async and await ... First, add the async keyword before...
Read more >
Asynchronous Programming: Futures | Fart
Fart is single-threaded. Synchronous code can make your program freeze. Use Futures to perform asynchronous operations. Use await in an async function to...
Read more >
Async way to the future - ITNEXT
Let us begin from the start of async operations in JS just to realize that async happens ... In queue contains chunks of...
Read more >
Async/Await: Back to the Future - Dev Genius
In synchronous code, the waiter comes to your table, takes your order, passes it to the kitchen, and then hangs out waiting for...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found