Skip to content

Commit 57d10d0

Browse files
committed
Adds flatten support for RequiresContextResult
1 parent a520f94 commit 57d10d0

File tree

12 files changed

+169
-33
lines changed

12 files changed

+169
-33
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ See [0Ver](https://0ver.org/).
2626
- Adds `RequiresContextResultE` alias
2727
for `RequiresContextResult[..., ..., Exception]`
2828
- Adds `RequiresContextResult` support for `bind` and `rescue`
29+
- Adds `RequiresContextResult` support for `flatten`
2930

3031
- Adds `IOResult` helper to work better with `IO[Result[a, b]]`
3132
- Adds `IOResultE` alias for `IOResult[a, Exception]`
@@ -37,7 +38,7 @@ See [0Ver](https://0ver.org/).
3738

3839
- Adds `Result.lift`, `Maybe.lift`, `RequiresContext.lift`,
3940
and `RequiresContextResult.lift` functions in addition to `IO.lift`
40-
- Adds
41+
- Adds `Immutable` primitive type
4142

4243

4344
### Bugfixes

docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ Contents
3636
pages/converters.rst
3737
pages/pointfree.rst
3838
pages/functions.rst
39+
pages/types.rst
3940

4041
.. toctree::
4142
:maxdepth: 1

docs/pages/container.rst

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,13 @@ Note::
100100
All containers support these methods.
101101

102102

103+
.. _immutability:
104+
103105
Immutability
104106
------------
105107

106-
We like to think of ``returns`` as immutable structures.
108+
We like to think of ``returns``
109+
as :ref:`immutable <primitive-types>` structures.
107110
You cannot mutate the inner state of the created container,
108111
because we redefine ``__setattr__`` and ``__delattr__`` magic methods.
109112

@@ -174,6 +177,13 @@ Needs transformation
174177
- ``Maybe[Result[A, B]]`` 🤔,
175178
use :func:`result_to_maybe <returns.converters.result_to_maybe>`
176179
and then :func:`flatten <returns.converters.flatten>`
180+
- ``RequiresContext[env, Result[A, B]]`` 🤔,
181+
use :meth:`returns.context.requires_context_result.from_typecast`
182+
and ``RequiresResultContext``
183+
- ``RequiresContext[env, RequiresContext[env, A]]`` 🤔,
184+
use :func:`flatten <returns.converters.flatten>`
185+
- ``RequiresContextResult[env, RequiresContextResult[env, A, B], B]`` 🤔,
186+
use :func:`flatten <returns.converters.flatten>`
177187

178188
Nope
179189
~~~~

docs/pages/context.rst

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ Let's see how our code changes:
136136
137137
And now you can pass your dependencies in a really direct and explicit way.
138138

139+
.. _ask:
140+
139141
ask
140142
~~~
141143

@@ -277,15 +279,71 @@ Use it when you work with pure context-related functions that might fail.
277279
FAQ
278280
---
279281

280-
Why do I have to use explicit type annotation for ask method?
281-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
282+
Why can’t we use RequiresContext[e, Result] instead of RequiresContextResult?
283+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
284+
285+
We actually can! But, it is harder to write.
286+
And ``RequiresContextResult`` is actually
287+
the very same thing as ``RequiresContext[e, Result]``, but has nicer API:
288+
289+
.. code:: python
290+
291+
x: RequiresContext[int, Result[int, str]]
292+
x.map(lambda result: result.map(lambda number: number + 1))
293+
294+
# Is the same as:
295+
296+
y: RequiresContextResult[int, int, str]
297+
y.map(lambda number: number + 1)
298+
299+
The second one looks better, doesn't it?
282300

283301
How to create unit objects?
284302
~~~~~~~~~~~~~~~~~~~~~~~~~~~
285303

304+
``RequiresContext`` allows you to create
305+
unit values with the help of ``.from_value`` method:
306+
307+
.. code:: python
308+
309+
>>> from returns.context import RequiresContext
310+
>>> assert RequiresContext.from_value(1)(...) == 1
311+
312+
``RequiresContextResult`` requires you to use one of the following methods:
313+
314+
- ``from_success`` when you want to mark some raw value as a ``Success``
315+
- ``from_failure`` when you want to mark some raw value as a ``Failure``
316+
- ``from_result`` when you already have one
317+
- ``from_successful_context`` when you have successful ``RequiresContext``
318+
- ``from_failed_context`` when you have failed ``RequiresContext``
319+
320+
But, think twice: why would you need to do it?
321+
These classes represent computations that rely on context.
322+
Maybe, you should not do creat their units?
323+
286324
How can I access dependencies inside the context?
287325
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
288326

327+
Use ``.ask()`` method!
328+
329+
See :ref:`this guide <ask>`.
330+
331+
Why do I have to use explicit type annotation for ask method?
332+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
333+
334+
Because ``mypy`` cannot possibly know the type of current context.
335+
This is hard even for a plugin.
336+
337+
So, using this technique is better:
338+
339+
.. code:: python
340+
341+
from returns.context import Context, RequiresContext
342+
343+
def some_context(*args, **kwargs) -> RequiresContext[int, str]:
344+
def factory(deps: int) -> RequiresContext[int, str]:
345+
...
346+
return Context[int].ask().bind(factory)
289347
290348
291349
Further reading

docs/pages/io.rst

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -454,19 +454,20 @@ but has nicer API:
454454
455455
The second one looks better, doesn't it?
456456

457-
How to create unit object for IOResult?
458-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
457+
How to create unit objects for IOResult?
458+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
459459

460-
*TLDR*: you need to explicitly annotate values like so:
460+
*TLDR*: you need to use ``IOSuccess`` and ``IOFailure``:
461461

462462
.. code:: python
463463
464464
>>> from returns.io import IOResult, IOSuccess, IOFailure
465465
>>> first: IOResult[int, str] = IOSuccess(1)
466466
>>> second: IOResult[float, int] = IOFailure(1)
467467
468+
You can also annotate your variables properly.
468469
Otherwise, ``mypy`` will treat ``IOSuccess(1)`` as ``IOSuccess[int, Any]``.
469-
You need to narrow the type.
470+
You can narrow the type in advance.
470471

471472
See :ref:`result-units` for more details.
472473

@@ -491,7 +492,8 @@ Warning::
491492
the internal state of the IO with ``._internal_state``,
492493
but your are considered to be a grown-up!
493494

494-
Use wemake-python-styleguide to restrict `._` access in your code.
495+
Use `wemake-python-styleguide <https://github.com/wemake-services/wemake-python-styleguide>`_
496+
to restrict ``._`` access in your code.
495497

496498

497499
Further reading

docs/pages/types.rst

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
.. _primitive-types:
2+
3+
Primitive types
4+
===============
5+
6+
We have several utility types that we use for our containers,
7+
that can also help end users as well.
8+
9+
10+
Immutable
11+
---------
12+
13+
This class is useful when you need
14+
to make some instances immutable
15+
(like :ref:`our containers are <immutability>`).
16+
17+
18+
API Reference
19+
-------------
20+
21+
.. autoclasstree:: returns.primitives.types
22+
23+
.. automodule:: returns.primitives.types
24+
:members:

returns/_generated/flatten.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# -*- coding: utf-8 -*-
22

3+
from returns.functions import identity
4+
35

46
def _flatten(container):
57
"""
@@ -15,7 +17,7 @@ def _flatten(container):
1517
>>> from returns.maybe import Some
1618
>>> from returns.result import Failure, Success
1719
>>> from returns.io import IO, IOSuccess, IOFailure
18-
>>> from returns.context import RequiresContext
20+
>>> from returns.context import RequiresContext, RequiresContextResult
1921
2022
>>> assert flatten(IO(IO(1))) == IO(1)
2123
@@ -31,8 +33,14 @@ def _flatten(container):
3133
... RequiresContext.from_value(RequiresContext.from_value(1)),
3234
... )(RequiresContext.empty) == 1
3335
36+
>>> assert flatten(
37+
... RequiresContextResult.from_success(
38+
... RequiresContextResult.from_success(1),
39+
... ),
40+
... )(RequiresContext.empty) == Success(1)
41+
3442
See also:
3543
https://bit.ly/2sIviUr
3644
3745
"""
38-
return container.bind(lambda identity: identity) # we cannot import it :(
46+
return container.bind(identity)

returns/_generated/flatten.pyi

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from typing import TypeVar, overload
44

5-
from returns.context import RequiresContext
5+
from returns.context import RequiresContext, RequiresContextResult
66
from returns.io import IO, IOResult
77
from returns.maybe import Maybe
88
from returns.result import Result
@@ -43,3 +43,15 @@ def _flatten(
4343
],
4444
) -> RequiresContext[_EnvType, _ValueType]:
4545
...
46+
47+
48+
49+
@overload
50+
def _flatten(
51+
container: RequiresContextResult[
52+
_EnvType,
53+
RequiresContextResult[_EnvType, _ValueType, _ErrorType],
54+
_ErrorType,
55+
],
56+
) -> RequiresContextResult[_EnvType, _ValueType, _ErrorType]:
57+
...

returns/context/requires_context.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
from returns.functions import identity
88
from returns.primitives.container import BaseContainer
9-
from returns.primitives.types import Immutable, Stateless
9+
from returns.primitives.types import Immutable
1010

1111
# Context:
1212
_EnvType = TypeVar('_EnvType', contravariant=True)
@@ -226,11 +226,7 @@ def from_value(
226226

227227

228228
@final
229-
class Context(
230-
Immutable,
231-
Stateless,
232-
Generic[_EnvType],
233-
):
229+
class Context(Immutable, Generic[_EnvType]):
234230
"""
235231
Helpers that can be used to work with ``RequiresContext`` container.
236232

returns/context/requires_context_result.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
from returns.context.requires_context import RequiresContext
88
from returns.primitives.container import BaseContainer
9-
from returns.primitives.types import Immutable, Stateless
9+
from returns.primitives.types import Immutable
1010
from returns.result import Failure, Result, Success
1111

1212
# Context:
@@ -649,11 +649,7 @@ def from_failure(
649649

650650

651651
@final
652-
class ContextResult(
653-
Immutable,
654-
Stateless,
655-
Generic[_EnvType],
656-
):
652+
class ContextResult(Immutable, Generic[_EnvType]):
657653
"""
658654
Helpers that can be used to work with ``RequiresContextResult`` container.
659655

0 commit comments

Comments
 (0)