Skip to content

Commit d41c2bc

Browse files
committed
Closes #238, closes #236
1 parent 4a7486e commit d41c2bc

File tree

21 files changed

+209
-185
lines changed

21 files changed

+209
-185
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ See (0Ver)[https://0ver.org/].
1313
- **Breaking**: renames `join` to `flatten`, sorry!
1414
- **Breaking**: renames `box` to `bind` and moves it to `returns.pointfree`
1515
- **Breaking**: renames `coalesce` to `fold`
16+
- **Breaking**: removes `Maybe.rescue` and `Maybe.fix` methods
17+
- Adds `rescue` pointfree method
1618
- Adds `Result.lift`, `Maybe.lift`, and `RequiresContext.lift` functions
1719
in addition to `IO.lift`
1820
- Adds `RequiresContext` container and `Context` helper class

docs/pages/maybe.rst

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,37 @@ and converts it to return ``Maybe`` instead:
129129
'<Some: 1>'
130130
131131
132+
FAQ
133+
---
134+
135+
How can I turn Maybe into Optional again?
136+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
137+
138+
When working with regular Python,
139+
you might need regular ``Optional[a]`` values.
140+
141+
You can easily get one from your ``Maybe`` container at any point in time:
142+
143+
.. code:: python
144+
145+
>>> from returns.maybe import Maybe
146+
>>> assert Maybe.new(1).value_or(None) == 1
147+
>>> assert Maybe.new(None).value_or(None) is None
148+
149+
As you can see, revealed type of ``.value_or(None)`` is ``Optional[a]``.
150+
Use it a fallback.
151+
152+
Why there's no IOMaybe?
153+
~~~~~~~~~~~~~~~~~~~~~~~
154+
155+
We do have ``IOResult``, but we don't have ``IOMaybe``. Why?
156+
Because when dealing with ``IO`` there are a lot of possible errors.
157+
And ``Maybe`` represents just ``None`` and the value.
158+
159+
It is not useful for ``IO`` related tasks.
160+
So, use ``Result`` instead, which can represent what happened to your ``IO``.
161+
162+
132163
Further reading
133164
---------------
134165

returns/_generated/fold.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@ def decorator(container):
7777

7878
_fold_ioresult: Callable[
7979
[
80-
Callable[[_ValueType], IO[_FirstType]],
81-
Callable[[_ErrorType], IO[_FirstType]],
80+
Callable[[IO[_ValueType]], IO[_FirstType]],
81+
Callable[[IO[_ErrorType]], IO[_FirstType]],
8282
],
8383
Callable[[IOResult[_ValueType, _ErrorType]], IO[_FirstType]],
8484
] = _fold

returns/_generated/pointfree/bind.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,23 @@ def _bind(function):
88
In other words, it modifies the function
99
signature from: ``a -> Container[b]`` to: ``Container[a] -> Container[b]``
1010
11+
Similar to :func:`returns.pointfree.rescue`,
12+
but works for successful containers.
13+
1114
This is how it should be used:
1215
1316
.. code:: python
1417
1518
>>> from returns.pointfree import bind
16-
>>> from returns.maybe import Maybe, Some
19+
>>> from returns.maybe import Maybe, Some, Nothing
1720
1821
>>> def example(argument: int) -> Maybe[int]:
1922
... return Some(argument + 1)
2023
...
21-
>>> bind(example)(Some(1)) == Some(2)
22-
True
24+
>>> assert bind(example)(Some(1)) == Some(2)
25+
>>> assert bind(example)(Nothing) == Nothing
26+
27+
Note, that this function works for all containers with ``.bind`` method.
2328
2429
"""
2530
return lambda container: container.bind(function)

returns/_generated/pointfree/bind.pyi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ _NewValueType = TypeVar('_NewValueType')
1313
_EnvType = TypeVar('_EnvType')
1414

1515

16-
# Box:
16+
# Bind:
1717

1818
@overload
1919
def _bind(
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# -*- coding: utf-8 -*-
2+
3+
4+
def _rescue(function):
5+
"""
6+
Boxes function's input parameter from a regular value to a container.
7+
8+
In other words, it modifies the function
9+
signature from: ``a -> Container[b]`` to: ``Container[a] -> Container[b]``
10+
11+
Similar to :func:`returns.pointfree.bind`, but works for failed containers.
12+
13+
This is how it should be used:
14+
15+
.. code:: python
16+
17+
>>> from returns.pointfree import rescue
18+
>>> from returns.result import Success, Failure, Result
19+
20+
>>> def example(argument: int) -> Result[str, int]:
21+
... return Success(argument + 1)
22+
...
23+
>>> assert rescue(example)(Success('a')) == Success('a')
24+
>>> assert rescue(example)(Failure(1)) == Success(2)
25+
26+
Note, that this function works for all containers with ``.bind`` method.
27+
28+
"""
29+
return lambda container: container.rescue(function)
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# -*- coding: utf-8 -*-
2+
3+
from typing import Callable, TypeVar, overload
4+
5+
from returns.io import IOResult
6+
from returns.result import Result
7+
8+
_ValueType = TypeVar('_ValueType')
9+
_ErrorType = TypeVar('_ErrorType')
10+
_NewValueType = TypeVar('_NewValueType')
11+
_NewErrorType = TypeVar('_NewErrorType')
12+
13+
14+
@overload
15+
def _rescue(
16+
function: Callable[[_ErrorType], Result[_ValueType, _NewErrorType]],
17+
) -> Callable[
18+
[Result[_ValueType, _ErrorType]],
19+
Result[_ValueType, _NewErrorType],
20+
]:
21+
...
22+
23+
24+
@overload
25+
def _rescue(
26+
function: Callable[[_ErrorType], IOResult[_ValueType, _NewErrorType]],
27+
) -> Callable[
28+
[IOResult[_ValueType, _ErrorType]],
29+
IOResult[_ValueType, _NewErrorType],
30+
]:
31+
...

returns/io.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,21 @@ def fix(
367367
"""
368368
return self.from_result(self._inner_value.fix(function))
369369

370+
def alt(
371+
self,
372+
function: Callable[[_ErrorType], _NewErrorType],
373+
) -> 'IOResult[_ValueType, _NewErrorType]':
374+
"""
375+
Composes failed container with a pure function to modify failure.
376+
377+
.. code:: python
378+
379+
>>> from returns.io import IOFailure
380+
>>> assert IOFailure(1).alt(float) == IOFailure(1.0)
381+
382+
"""
383+
return self.from_result(self._inner_value.alt(function))
384+
370385
def rescue(
371386
self,
372387
function: Callable[
@@ -392,21 +407,6 @@ def rescue(
392407
"""
393408
raise NotImplementedError
394409

395-
def alt(
396-
self,
397-
function: Callable[[_ErrorType], _NewErrorType],
398-
) -> 'IOResult[_ValueType, _NewErrorType]':
399-
"""
400-
Composes failed container with a pure function to modify failure.
401-
402-
.. code:: python
403-
404-
>>> from returns.io import IOFailure
405-
>>> assert IOFailure(1).alt(float) == IOFailure(1.0)
406-
407-
"""
408-
return self.from_result(self._inner_value.alt(function))
409-
410410
def value_or(
411411
self,
412412
default_value: _NewValueType,

returns/maybe.py

Lines changed: 0 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -108,44 +108,6 @@ def bind(
108108
"""
109109
raise NotImplementedError
110110

111-
def fix(
112-
self,
113-
function: Callable[[None], Optional[_NewValueType]],
114-
) -> 'Maybe[_NewValueType]':
115-
"""
116-
Composes failed container with a pure function to fix the failure.
117-
118-
.. code:: python
119-
120-
>>> from returns.maybe import Nothing, Some
121-
>>> def fixable(_state) -> str:
122-
... return 'ab'
123-
...
124-
>>> assert Some('a').fix(fixable) == Some('a')
125-
>>> assert Nothing.fix(fixable) == Some('ab')
126-
127-
"""
128-
raise NotImplementedError
129-
130-
def rescue(
131-
self,
132-
function: Callable[[None], 'Maybe[_NewValueType]'],
133-
) -> 'Maybe[_NewValueType]':
134-
"""
135-
Composes failed container with a function that returns a container.
136-
137-
.. code:: python
138-
139-
>>> from returns.maybe import Nothing, Maybe, Some
140-
>>> def rescuable(_state) -> Maybe[str]:
141-
... return Some('ab')
142-
...
143-
>>> assert Some('a').rescue(rescuable) == Some('a')
144-
>>> assert Nothing.rescue(rescuable) == Some('ab')
145-
146-
"""
147-
raise NotImplementedError
148-
149111
def value_or(
150112
self,
151113
default_value: _NewValueType,
@@ -262,14 +224,6 @@ def bind(self, function):
262224
"""Does nothing for ``Nothing``."""
263225
return self
264226

265-
def fix(self, function):
266-
"""Composes pure function with a failed container."""
267-
return Maybe.new(function(self._inner_value))
268-
269-
def rescue(self, function):
270-
"""Composes failed container with a function that returns container."""
271-
return function(self._inner_value)
272-
273227
def value_or(self, default_value):
274228
"""Returns default value."""
275229
return default_value
@@ -310,14 +264,6 @@ def bind(self, function):
310264
"""Binds current container to a function that returns container."""
311265
return function(self._inner_value)
312266

313-
def fix(self, function):
314-
"""Does nothing."""
315-
return self
316-
317-
def rescue(self, function):
318-
"""Does nothing."""
319-
return self
320-
321267
def value_or(self, default_value):
322268
"""Returns inner value for successful container."""
323269
return self._inner_value

returns/pointfree.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
# -*- coding: utf-8 -*-
22

33
from returns._generated.pointfree.bind import _bind as bind # noqa: F401
4+
from returns._generated.pointfree.rescue import _rescue as rescue # noqa: F401

0 commit comments

Comments
 (0)