Skip to content

Commit 916c9b7

Browse files
committed
Adds .bind_context_ioresul, refs #274
1 parent d630305 commit 916c9b7

File tree

14 files changed

+163
-111
lines changed

14 files changed

+163
-111
lines changed

docs/pages/pointfree.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,8 @@ API Reference
241241

242242
.. autofunction:: returns.pointfree.bind_context_result
243243

244+
.. autofunction:: returns.pointfree.bind_context_ioresult
245+
244246
.. autofunction:: returns.pointfree.bind_async
245247

246248
.. autofunction:: returns.pointfree.unify
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
def _bind_context_ioresult(function):
2+
"""
3+
Lifts function from ``RequiresContextIOResult`` for better composition.
4+
5+
In other words, it modifies the function's
6+
signature from: ``a -> RequiresContextResult[env, b, c]`` to:
7+
``Container[env, a, c]`` -> ``Container[env, b, c]``
8+
9+
.. code:: python
10+
11+
>>> import anyio
12+
>>> from returns.context import (
13+
... RequiresContextFutureResult,
14+
... RequiresContextIOResult,
15+
... )
16+
>>> from returns.io import IOSuccess, IOFailure
17+
>>> from returns.pointfree import bind_context_ioresult
18+
19+
>>> def function(arg: int) -> RequiresContextIOResult[str, int, str]:
20+
... return RequiresContextIOResult(
21+
... lambda deps: IOSuccess(len(deps) + arg),
22+
... )
23+
24+
>>> assert anyio.run(bind_context_ioresult(function)(
25+
... RequiresContextFutureResult.from_value(2),
26+
... )('abc').awaitable) == IOSuccess(5)
27+
28+
>>> assert anyio.run(bind_context_ioresult(function)(
29+
... RequiresContextFutureResult.from_failure(0),
30+
... )('abc').awaitable) == IOFailure(0)
31+
32+
"""
33+
return lambda container: container.bind_context_ioresult(function)
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
from typing import Callable, TypeVar
2+
3+
from typing_extensions import Protocol
4+
5+
from returns.context import RequiresContextFutureResult, RequiresContextIOResult
6+
7+
_ValueType = TypeVar('_ValueType', contravariant=True)
8+
_ErrorType = TypeVar('_ErrorType')
9+
_NewValueType = TypeVar('_NewValueType', covariant=True)
10+
_EnvType = TypeVar('_EnvType')
11+
12+
13+
class _BindContextIOResult(
14+
Protocol[_EnvType, _ValueType, _NewValueType, _ErrorType],
15+
):
16+
"""
17+
Helper class to represent type overloads for ret_type based on a value type.
18+
19+
Contains all containers we have.
20+
21+
It does not exist in runtime.
22+
It is also completely removed from typing with the help of the mypy plugin.
23+
"""
24+
25+
def __call__(
26+
self,
27+
container: RequiresContextFutureResult[
28+
_EnvType, _ValueType, _ErrorType,
29+
],
30+
) -> RequiresContextFutureResult[_EnvType, _NewValueType, _ErrorType]:
31+
...
32+
33+
34+
def _bind_context_ioresult(
35+
function: Callable[
36+
[_ValueType],
37+
RequiresContextIOResult[_EnvType, _NewValueType, _ErrorType],
38+
],
39+
) -> _BindContextIOResult[_EnvType, _ValueType, _NewValueType, _ErrorType]:
40+
...

returns/context/requires_context_future_result.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,48 @@ def bind_context_result(
466466
),
467467
)
468468

469+
def bind_context_ioresult(
470+
self,
471+
function: Callable[
472+
[_ValueType],
473+
'RequiresContextIOResult[_EnvType, _NewValueType, _ErrorType]',
474+
],
475+
) -> 'RequiresContextFutureResult[_EnvType, _NewValueType, _ErrorType]':
476+
"""
477+
Binds ``RequiresContextIOResult`` returning function to the current one.
478+
479+
.. code:: python
480+
481+
>>> import anyio
482+
>>> from returns.context import RequiresContextIOResult
483+
>>> from returns.io import IOSuccess, IOFailure
484+
485+
>>> def function(arg: int) -> RequiresContextIOResult[str, int, int]:
486+
... return RequiresContextIOResult(
487+
... lambda deps: IOSuccess(len(deps) + arg),
488+
... )
489+
490+
>>> instance = RequiresContextFutureResult.from_value(
491+
... 2,
492+
... ).bind_context_ioresult(
493+
... function,
494+
... )('abc')
495+
>>> assert anyio.run(instance.awaitable) == IOSuccess(5)
496+
497+
>>> instance = RequiresContextFutureResult.from_failure(
498+
... 2,
499+
... ).bind_context_ioresult(
500+
... function,
501+
... )('abc')
502+
>>> assert anyio.run(instance.awaitable) == IOFailure(2)
503+
504+
"""
505+
return RequiresContextFutureResult(
506+
lambda deps: self(deps).bind_ioresult(
507+
lambda inner: function(inner)(deps), # type: ignore[misc]
508+
),
509+
)
510+
469511
def bind_io(
470512
self,
471513
function: Callable[[_ValueType], IO[_NewValueType]],

returns/contrib/mypy/returns_plugin.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
'returns._generated.pointfree.bind_future_result._bind_future_result',
5757
'returns._generated.pointfree.bind_context._bind_context',
5858
'returns._generated.pointfree.bind_context_result._bind_context_result',
59+
'returns._generated.pointfree.bind_context_ioresult._bind_context_ioresult',
5960
'returns._generated.pointfree.bind_awaitable._bind_awaitable',
6061

6162
'returns._generated.pointfree.value_or._value_or',

returns/pointfree.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
from returns._generated.pointfree.bind_context import (
99
_bind_context as bind_context,
1010
)
11+
from returns._generated.pointfree.bind_context_ioresult import (
12+
_bind_context_ioresult as bind_context_ioresult,
13+
)
1114
from returns._generated.pointfree.bind_context_result import (
1215
_bind_context_result as bind_context_result,
1316
)

typesafety/test_context/test_requires_context_future_result/test_requires_context_future_result.yml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,6 @@
128128
reveal_type(x.bind_context(test)) # N: Revealed type is 'returns.context.requires_context_future_result.RequiresContextFutureResult[builtins.str, builtins.bool*, builtins.float]'
129129
130130
131-
132131
- case: requires_context_future_result_bind_context_result
133132
disable_cache: true
134133
main: |
@@ -142,6 +141,19 @@
142141
reveal_type(x.bind_context_result(test)) # N: Revealed type is 'returns.context.requires_context_future_result.RequiresContextFutureResult[builtins.str, builtins.bool*, builtins.float]'
143142
144143
144+
- case: requires_context_future_result_bind_context_ioresult
145+
disable_cache: true
146+
main: |
147+
from returns.context import RequiresContextFutureResult, RequiresContextIOResult
148+
149+
x: RequiresContextFutureResult[str, int, float]
150+
151+
def test(param: int) -> RequiresContextIOResult[str, bool, float]:
152+
...
153+
154+
reveal_type(x.bind_context_ioresult(test)) # N: Revealed type is 'returns.context.requires_context_future_result.RequiresContextFutureResult[builtins.str, builtins.bool*, builtins.float]'
155+
156+
145157
- case: requires_context_future_result_rescue
146158
disable_cache: true
147159
main: |

typesafety/test_pointfree/test_bind_awaitable.yml

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,6 @@
1616
reveal_type(flow(r, bind_awaitable(test), bind_awaitable(second), identity)) # N: Revealed type is 'returns.future.FutureResult*[builtins.bool, builtins.str*]'
1717
1818
19-
- case: bind_awaitable_and_bind
20-
disable_cache: True
21-
main: |
22-
from returns.future import FutureResult
23-
from returns.functions import identity
24-
from returns.pointfree import bind, bind_awaitable
25-
from returns.pipeline import flow
26-
27-
async def test(arg: int) -> float:
28-
...
29-
30-
def second(arg: float) -> FutureResult[bool, str]:
31-
...
32-
33-
r: FutureResult[int, str]
34-
reveal_type(flow(r, bind_awaitable(test), bind(second))) # N: Revealed type is 'returns.future.FutureResult[builtins.bool*, builtins.str*]'
35-
36-
3719
- case: bind_awaitable_future
3820
disable_cache: true
3921
main: |

typesafety/test_pointfree/test_bind_context.yml

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -32,24 +32,6 @@
3232
reveal_type(flow(r, bind_context(test), bind_context(second), identity)) # N: Revealed type is 'returns.context.requires_context_result.RequiresContextResult*[builtins.object, builtins.bool, builtins.str*]'
3333
3434
35-
- case: bind_context_and_bind
36-
disable_cache: true
37-
main: |
38-
from returns.context import RequiresContext, RequiresContextResult
39-
from returns.functions import identity
40-
from returns.pointfree import bind, bind_context
41-
from returns.pipeline import flow
42-
43-
def test(arg: int) -> RequiresContext[object, float]:
44-
...
45-
46-
def second(arg: float) -> RequiresContextResult[object, bool, str]:
47-
...
48-
49-
r: RequiresContextResult[object, int, str]
50-
reveal_type(flow(r, bind_context(test), bind(second), identity)) # N: Revealed type is 'returns.context.requires_context_result.RequiresContextResult*[builtins.object*, builtins.bool*, builtins.str*]'
51-
52-
5335
- case: bind_context_requires_context_result
5436
disable_cache: true
5537
main: |
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
- case: bind_context_ioresult_and_flow
2+
disable_cache: true
3+
main: |
4+
from returns.context import RequiresContextFutureResult, RequiresContextIOResult
5+
from returns.functions import identity
6+
from returns.pointfree import bind_context_ioresult
7+
from returns.pipeline import flow
8+
9+
def test(arg: int) -> RequiresContextIOResult[object, float, str]:
10+
...
11+
12+
def second(arg: float) -> RequiresContextIOResult[object, bool, str]:
13+
...
14+
15+
r: RequiresContextFutureResult[object, int, str]
16+
reveal_type(flow(r, bind_context_ioresult(test), bind_context_ioresult(second), identity)) # N: Revealed type is 'returns.context.requires_context_future_result.RequiresContextFutureResult*[builtins.object*, builtins.bool*, builtins.str*]'
17+
18+
19+
- case: bind_context_ioresult_requires_context_future_result
20+
disable_cache: true
21+
main: |
22+
from returns.pointfree import bind_context_ioresult
23+
from returns.context import RequiresContextIOResult, RequiresContextFutureResult
24+
25+
def test(arg: float) -> RequiresContextIOResult[bool, int, str]:
26+
...
27+
28+
x: RequiresContextFutureResult[bool, float, str]
29+
reveal_type(bind_context_ioresult(test)(x)) # N: Revealed type is 'returns.context.requires_context_future_result.RequiresContextFutureResult[builtins.bool*, builtins.int*, builtins.str*]'

0 commit comments

Comments
 (0)