Skip to content

Commit 50e6368

Browse files
committed
Closes #96, closes #106, closes #105
1 parent 818c764 commit 50e6368

File tree

10 files changed

+155
-50
lines changed

10 files changed

+155
-50
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ We follow Semantic Versions since the `0.1.0` release.
88
### Features
99

1010
- Provides a bunch of primitive interfaces to write your own containers
11+
- Adds `.map_failure()` method
12+
- Adds `join()` function to join nested containers
1113

1214
### Bugfixes
1315

docs/pages/container.rst

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ The difference is simple:
5050
- ``map`` works with functions that return regular value
5151
- ``bind`` works with functions that return new container of the same type
5252

53-
:func:`.bind <returns.primitives.container.Bindable.bind>`
53+
:func:`~returns.primitives.container.Bindable.bind`
5454
is used to literally bind two different containers together.
5555

5656
.. code:: python
@@ -63,7 +63,7 @@ is used to literally bind two different containers together.
6363
# Can be assumed as either Success[int] or Failure[str]:
6464
result: Result[int, str] = Success(1).bind(may_fail)
6565
66-
And we have :func:`.map <returns.primitives.container.Mappable.map>`
66+
And we have :func:`~returns.primitives.container.Mappable.map`
6767
to use containers with regular functions.
6868

6969
.. code:: python
@@ -140,13 +140,17 @@ Returning execution to the right track
140140
We also support two special methods to work with "failed"
141141
types like ``Failure``:
142142

143-
- :func:`.rescue <returns.primitives.container.Rescueable.rescue>`
143+
- :func:`~returns.primitives.container.Rescueable.rescue`
144144
is the opposite of ``bind`` method
145145
that works only when container is in failed state
146-
- :func:`.fix <returns.primitives.container.Fixable.fix>`
146+
- :func:`~returns.primitives.container.Fixable.fix`
147147
transforms error to value (failure became success)
148148
that works only when container is in failed state,
149149
is the opposite of ``map`` method
150+
- :func:`~returns.primitives.container.UnwrapableFailure.map_failure`
151+
transforms error to another error
152+
that works only when container is in failed state,
153+
is the opposite of ``map`` method
150154

151155
``fix`` can be used to fix some fixable errors
152156
during the pipeline execution:
@@ -158,7 +162,7 @@ during the pipeline execution:
158162
def double(state: int) -> float:
159163
return state * 2.0
160164
161-
result: Result[Any, float] = Failure(1).map_error(double)
165+
result: Result[Any, float] = Failure(1).map_failure(double)
162166
# => Failure(2.0)
163167
164168
result: Result[float, int] = Failure(1).fix(double)
@@ -301,20 +305,26 @@ You can and should compose different containers together.
301305
Here's the full table of compositions that make sense:
302306

303307
- ``IO[Result[A, B]]`` ✅
304-
- ``Result[IO[A], B]`` ✅
305308
- ``IO[Maybe[A]]`` ✅
306-
- ``Maybe[IO[A]]`` ✅
307-
- ``IO[IO[A]]`` 🚫
309+
- ``IO[IO[A]]`` 🤔, use :func:`join <returns.converters.join>`
310+
- ``Maybe[Maybe[A]]`` 🤔, use :func:`join <returns.converters.join>`
311+
- ``Result[Result[A, B], C]`` 🤔, use :func:`join <returns.converters.join>`
312+
- ``Result[Maybe[A], B]`` 🤔,
313+
use :func:`maybe_to_result <returns.converters.maybe_to_result>`
314+
- ``Maybe[Result[A, B]]`` 🤔,
315+
use :func:`result_to_maybe <returns.converters.result_to_maybe>`
316+
- ``Result[IO[A], B]`` 🚫
308317
- ``Result[A, IO[A]]`` 🚫
309-
- ``Result[Maybe[A], B]`` 🚫
310318
- ``Result[A, Maybe[B]]`` 🚫
311-
- ``Result[Result[A, B], C]`` 🚫
312319
- ``Result[A, Result[B, C]]`` 🚫
313-
- ``Maybe[Result[A, B]]`` 🚫
320+
- ``Maybe[IO[A]]`` 🚫
314321

315322
You can use :ref:`converters` to convert ``Maybe`` and ``Result`` containers.
316323
So, you don't have to compose them.
317324

325+
You can also use :func:`join <returns.converters.join>`
326+
to merge nested containers.
327+
318328

319329
.. _converters:
320330

@@ -342,6 +352,19 @@ That's how they work:
342352
Take a note, that type changes.
343353
Also, take a note that ``Success(None)`` will be converted to ``Nothing``.
344354

355+
You can also use ``join`` to merge nested containers together:
356+
357+
.. code:: python
358+
359+
from returns.converters import join
360+
from returns.maybe import Maybe
361+
from returns.result import Success
362+
from returns.io import IO
363+
364+
assert join(IO(IO(1))) == IO(1)
365+
assert Maybe(Maybe(1)) == Maybe(1)
366+
assert Success(Success(1)) == Success(1)
367+
345368
346369
API Reference
347370
-------------
@@ -350,3 +373,6 @@ API Reference
350373

351374
.. automodule:: returns.primitives.container
352375
:members:
376+
377+
.. automodule:: returns.converters
378+
:members:

poetry.lock

Lines changed: 20 additions & 20 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

returns/converters.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# -*- coding: utf-8 -*-
22

3-
from typing import TypeVar
3+
from typing import TypeVar, overload
44

5+
from returns.io import IO
56
from returns.maybe import Maybe
67
from returns.result import Failure, Result, Success
78

@@ -24,3 +25,25 @@ def maybe_to_result(
2425
if inner_value is not None:
2526
return Success(inner_value)
2627
return Failure(inner_value)
28+
29+
30+
@overload
31+
def join(container: IO[IO[_ValueType]]) -> IO[_ValueType]:
32+
"""Case for ``IO`` container."""
33+
34+
35+
@overload
36+
def join(container: Maybe[Maybe[_ValueType]]) -> Maybe[_ValueType]:
37+
"""Case for ``Maybe`` container."""
38+
39+
40+
@overload
41+
def join(
42+
container: Result[Result[_ValueType, _ErrorType], _ErrorType],
43+
) -> Result[_ValueType, _ErrorType]:
44+
"""Case for ``Result`` container."""
45+
46+
47+
def join(container):
48+
"""Joins two nested containers together."""
49+
return container._inner_value # noqa: Z441

returns/primitives/container.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -130,18 +130,6 @@ def rescue(
130130
Is the opposite of :meth:`~bind`.
131131
"""
132132

133-
def map_failure(
134-
self,
135-
function: Callable[[_ErrorType], _NewErrorType],
136-
) -> 'Fixable[_ValueType, _NewErrorType]':
137-
"""
138-
Uses 'function' to transform one error to another.
139-
140-
And returns a new functor value.
141-
Works for containers that represent failure.
142-
Is the opposite of :meth:`~map`.
143-
"""
144-
145133

146134
@runtime
147135
class Unwrapable(Protocol[_ValueType]):
@@ -167,6 +155,18 @@ def unwrap(self) -> _ValueType:
167155
class UnwrapableFailure(Protocol[_ValueType, _ErrorType]):
168156
"""Allows to unwrap failures."""
169157

158+
def map_failure(
159+
self,
160+
function: Callable[[_ErrorType], _NewErrorType],
161+
) -> 'Fixable[_ValueType, _NewErrorType]':
162+
"""
163+
Uses 'function' to transform one error to another.
164+
165+
And returns a new functor value.
166+
Works for containers that represent failure.
167+
Is the opposite of :meth:`~map`.
168+
"""
169+
170170
def failure(self) -> _ErrorType:
171171
"""
172172
Custom magic method to unwrap inner value from the failed container.

returns/result.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,14 @@ def fix(
5252
"""Abstract method to compose container with pure function."""
5353
raise NotImplementedError()
5454

55+
@abstractmethod
56+
def map_failure(
57+
self,
58+
function: Callable[[_ErrorType], _NewErrorType],
59+
) -> 'Result[_ValueType, _NewErrorType]': # pragma: no cover
60+
"""Abstract method to compose container with pure function."""
61+
raise NotImplementedError()
62+
5563
@abstractmethod
5664
def rescue(
5765
self,

0 commit comments

Comments
 (0)