Skip to content

Commit 635f420

Browse files
committed
Adds bind_async_future and bind_async_future_result pointfree, closes #274
1 parent b968d69 commit 635f420

File tree

9 files changed

+233
-0
lines changed

9 files changed

+233
-0
lines changed

docs/pages/pointfree.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,10 @@ We also have a long list of other ``bind_*`` functions, like:
105105
- ``bind_result`` to bind functions returning ``Result`` container
106106
- ``bind_ioresult`` to bind functions returning ``IOResult`` container
107107
- ``bind_future`` to bind functions returning ``Future`` container
108+
- ``bind_async_future`` to bind async functions returning ``Future`` container
108109
- ``bind_future_result`` to bind functions returning ``FutureResult`` container
110+
- ``bind_async_future_result`` to bind async functions
111+
returning ``FutureResult`` container
109112
- ``bind_context`` to bind functions returning ``RequiresContext`` container
110113
- ``bind_context_result`` to bind functions
111114
returning ``RequiresContextResult`` container
@@ -241,8 +244,12 @@ API Reference
241244

242245
.. autofunction:: returns.pointfree.bind_future
243246

247+
.. autofunction:: returns.pointfree.bind_async_future
248+
244249
.. autofunction:: returns.pointfree.bind_future_result
245250

251+
.. autofunction:: returns.pointfree.bind_async_future_result
252+
246253
.. autofunction:: returns.pointfree.bind_context
247254

248255
.. autofunction:: returns.pointfree.bind_context_result
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
def _bind_async_future(function):
2+
"""
3+
Compose a container and ``async`` function returning a `container`.
4+
5+
In other words, it modifies the function's
6+
signature from: ``a -> Awaitable[Container[b]]``
7+
to: ``Container[a] -> Container[b]``
8+
9+
This is how it should be used:
10+
11+
.. code:: python
12+
13+
>>> import anyio
14+
>>> from returns.future import Future, FutureResult
15+
>>> from returns.io import IOSuccess, IOFailure
16+
>>> from returns.pointfree import bind_async_future
17+
18+
>>> async def coroutine(x: int) -> Future[str]:
19+
... return Future.from_value(str(x + 1))
20+
21+
>>> bound = bind_async_future(coroutine)(FutureResult.from_value(1))
22+
>>> assert anyio.run(bound.awaitable) == IOSuccess('2')
23+
24+
>>> bound = bind_async_future(coroutine)(FutureResult.from_failure(1))
25+
>>> assert anyio.run(bound.awaitable) == IOFailure(1)
26+
27+
"""
28+
return lambda container: container.bind_async_future(function)
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
from typing import Awaitable, Callable, TypeVar, overload
2+
3+
from typing_extensions import Protocol
4+
5+
from returns.context import RequiresContextFutureResult
6+
from returns.future import Future, FutureResult
7+
8+
_ValueType = TypeVar('_ValueType', contravariant=True)
9+
_ErrorType = TypeVar('_ErrorType')
10+
_NewValueType = TypeVar('_NewValueType', covariant=True)
11+
_EnvType = TypeVar('_EnvType')
12+
13+
14+
class _BindAsyncFuture(Protocol[_ValueType, _NewValueType]):
15+
"""
16+
Helper class to represent type overloads for ret_type based on a value type.
17+
18+
Contains all containers we have.
19+
20+
It does not exist in runtime.
21+
It is also completely removed from typing with the help of the mypy plugin.
22+
"""
23+
24+
@overload
25+
def __call__(
26+
self,
27+
container: FutureResult[_ValueType, _ErrorType],
28+
) -> FutureResult[_NewValueType, _ErrorType]:
29+
...
30+
31+
@overload
32+
def __call__(
33+
self,
34+
container: RequiresContextFutureResult[
35+
_EnvType, _ValueType, _ErrorType,
36+
],
37+
) -> RequiresContextFutureResult[_EnvType, _NewValueType, _ErrorType]:
38+
...
39+
40+
41+
def _bind_async_future(
42+
function: Callable[[_ValueType], Awaitable[Future[_NewValueType]]],
43+
) -> _BindAsyncFuture[_ValueType, _NewValueType]:
44+
...
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
def _bind_async_future_result(function):
2+
"""
3+
Compose a container and ``async`` function returning a ``FutureResult``.
4+
5+
In other words, it modifies the function's
6+
signature from: ``a -> Awaitable[FutureResult[b, c]]``
7+
to: ``Container[a, c] -> Container[b, c]``
8+
9+
This is how it should be used:
10+
11+
.. code:: python
12+
13+
>>> import anyio
14+
>>> from returns.future import FutureResult
15+
>>> from returns.context import ReaderFutureResult
16+
>>> from returns.io import IOSuccess, IOFailure
17+
>>> from returns.pointfree import bind_async_future_result
18+
19+
>>> async def coroutine(x: int) -> FutureResult[str, int]:
20+
... return FutureResult.from_value(str(x + 1))
21+
22+
>>> bound = bind_async_future_result(coroutine)(
23+
... ReaderFutureResult.from_value(1),
24+
... )
25+
>>> assert anyio.run(bound, ReaderFutureResult.empty) == IOSuccess('2')
26+
27+
>>> bound = bind_async_future_result(coroutine)(
28+
... ReaderFutureResult.from_failure(1),
29+
... )
30+
>>> assert anyio.run(bound, ReaderFutureResult.empty) == IOFailure(1)
31+
32+
"""
33+
return lambda container: container.bind_async_future_result(function)
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
from typing import Awaitable, Callable, TypeVar, overload
2+
3+
from typing_extensions import Protocol
4+
5+
from returns.context import RequiresContextFutureResult
6+
from returns.future import FutureResult
7+
8+
_ValueType = TypeVar('_ValueType', contravariant=True)
9+
_ErrorType = TypeVar('_ErrorType')
10+
_NewValueType = TypeVar('_NewValueType', covariant=True)
11+
_EnvType = TypeVar('_EnvType')
12+
13+
14+
class _BindAsyncFutureResult(Protocol[_ValueType, _NewValueType, _ErrorType]):
15+
"""
16+
Helper class to represent type overloads for ret_type based on a value type.
17+
18+
Contains all containers we have.
19+
20+
It does not exist in runtime.
21+
It is also completely removed from typing with the help of the mypy plugin.
22+
"""
23+
24+
def __call__(
25+
self,
26+
container: RequiresContextFutureResult[
27+
_EnvType, _ValueType, _ErrorType,
28+
],
29+
) -> RequiresContextFutureResult[_EnvType, _NewValueType, _ErrorType]:
30+
...
31+
32+
33+
def _bind_async_future_result(
34+
function: Callable[
35+
[_ValueType],
36+
Awaitable[FutureResult[_NewValueType, _ErrorType]],
37+
],
38+
) -> _BindAsyncFutureResult[_ValueType, _NewValueType, _ErrorType]:
39+
...

returns/contrib/mypy/returns_plugin.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,12 @@
5353
'returns._generated.pointfree.bind_io._bind_io',
5454
'returns._generated.pointfree.bind_ioresult._bind_ioresult',
5555
'returns._generated.pointfree.bind_future._bind_future',
56+
'returns._generated.pointfree.bind_async_future._bind_async_future',
5657
'returns._generated.pointfree.bind_future_result._bind_future_result',
58+
(
59+
'returns._generated.pointfree.bind_async_future_result.' +
60+
'_bind_async_future_result'
61+
),
5762
'returns._generated.pointfree.bind_context._bind_context',
5863
'returns._generated.pointfree.bind_context_result._bind_context_result',
5964
'returns._generated.pointfree.bind_context_ioresult._bind_context_ioresult',

returns/pointfree.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22
from returns._generated.pointfree.apply import _apply as apply
33
from returns._generated.pointfree.bind import _bind as bind
44
from returns._generated.pointfree.bind_async import _bind_async as bind_async
5+
from returns._generated.pointfree.bind_async_future import (
6+
_bind_async_future as bind_async_future,
7+
)
8+
from returns._generated.pointfree.bind_async_future_result import (
9+
_bind_async_future_result as bind_async_future_result,
10+
)
511
from returns._generated.pointfree.bind_awaitable import (
612
_bind_awaitable as bind_awaitable,
713
)
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
- case: bind_async_future_and_flow
2+
disable_cache: true
3+
main: |
4+
from returns.functions import identity
5+
from returns.pointfree import bind_async_future, bind
6+
from returns.future import Future, FutureResult
7+
from returns.pipeline import flow
8+
9+
async def test(arg: int) -> Future[float]:
10+
...
11+
12+
def second(arg: float) -> FutureResult[bool, str]:
13+
...
14+
15+
r: FutureResult[int, str]
16+
reveal_type(flow(r, bind_async_future(test), bind(second), identity)) # N: Revealed type is 'returns.future.FutureResult*[builtins.bool*, builtins.str*]'
17+
18+
19+
- case: bind_async_future_futureresult
20+
disable_cache: true
21+
main: |
22+
from returns.pointfree import bind_async_future
23+
from returns.future import Future, FutureResult
24+
25+
async def test(arg: float) -> Future[int]:
26+
...
27+
28+
x: FutureResult[float, str]
29+
reveal_type(bind_async_future(test)(x)) # N: Revealed type is 'returns.future.FutureResult[builtins.int, builtins.str*]'
30+
31+
32+
- case: bind_async_future_requires_context_future_result
33+
disable_cache: true
34+
main: |
35+
from returns.pointfree import bind_async_future
36+
from returns.future import Future
37+
from returns.context import ReaderFutureResult
38+
39+
async def test(arg: float) -> Future[int]:
40+
...
41+
42+
x: ReaderFutureResult[bool, float, Exception]
43+
reveal_type(bind_async_future(test)(x)) # N: Revealed type is 'returns.context.requires_context_future_result.RequiresContextFutureResult[builtins.bool*, builtins.int, builtins.Exception*]'
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
- case: bind_async_future_and_flow
2+
disable_cache: true
3+
main: |
4+
from returns.functions import identity
5+
from returns.pointfree import bind_async_future_result
6+
from returns.future import FutureResult
7+
from returns.context import ReaderFutureResult
8+
from returns.pipeline import flow
9+
10+
async def test(arg: int) -> FutureResult[float, str]:
11+
...
12+
13+
r: ReaderFutureResult[object, int, str]
14+
reveal_type(flow(r, bind_async_future_result(test), identity)) # N: Revealed type is 'returns.context.requires_context_future_result.RequiresContextFutureResult*[builtins.object*, builtins.float, builtins.str]'
15+
16+
17+
- case: bind_async_future_result_requires_context_future_result
18+
disable_cache: true
19+
main: |
20+
from returns.pointfree import bind_async_future_result
21+
from returns.future import FutureResult
22+
from returns.context import ReaderFutureResult
23+
24+
async def test(arg: float) -> FutureResult[int, Exception]:
25+
...
26+
27+
x: ReaderFutureResult[bool, float, Exception]
28+
reveal_type(bind_async_future_result(test)(x)) # N: Revealed type is 'returns.context.requires_context_future_result.RequiresContextFutureResult[builtins.bool*, builtins.int, builtins.Exception]'

0 commit comments

Comments
 (0)