Skip to content

Commit 8c8820b

Browse files
Allows cond HKT method & pointfree to work with SingleFailableN (#653)
* Allows `cond` HKT method to work with `SingleFailableN` * Allows `cond` pointfree to work with `SingleFailableN` * Update docs/pages/pointfree.rst Co-authored-by: Nikita Sobolev <mail@sobolevn.me>
1 parent 5f7620b commit 8c8820b

File tree

5 files changed

+131
-12
lines changed

5 files changed

+131
-12
lines changed

docs/pages/pointfree.rst

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -218,16 +218,19 @@ kind of manipulation.
218218
cond
219219
----
220220

221-
Sometimes we need to create ``DiverseFailableN`` containers
222-
(e.g. ``ResultLikeN``) based on a boolean expression, ``cond`` can help us.
221+
Sometimes we need to create ``SingleFailableN`` or ``DiverseFailableN``
222+
containers (e.g. ``Maybe``, ``ResultLikeN``) based on a boolean expression,
223+
``cond`` can help us.
224+
225+
Consider ``cond`` to be a functional ``if``.
223226

224227
See the example below:
225228

226229
.. code:: python
227230
228231
>>> from returns.pipeline import flow
229232
>>> from returns.pointfree import cond
230-
>>> from returns.result import Failure, Success
233+
>>> from returns.result import Result, Failure, Success
231234
232235
>>> def returns_boolean(arg: int) -> bool:
233236
... return bool(arg)
@@ -242,6 +245,24 @@ See the example below:
242245
... cond(Result, 'success', 'failure')
243246
... ) == Failure('failure')
244247
248+
Example using ``cond`` with the ``Maybe`` container:
249+
250+
.. code:: python
251+
252+
>>> from returns.pipeline import flow
253+
>>> from returns.pointfree import cond
254+
>>> from returns.maybe import Maybe, Some, Nothing
255+
256+
>>> assert flow(
257+
... returns_boolean(1),
258+
... cond(Maybe, 'success')
259+
... ) == Some('success')
260+
261+
>>> assert flow(
262+
... returns_boolean(0),
263+
... cond(Maybe, 'success')
264+
... ) == Nothing
265+
245266
246267
Further reading
247268
---------------

returns/methods/cond.py

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,50 @@
1-
from typing import Type, TypeVar
1+
from typing import Optional, Type, TypeVar, Union, overload
22

33
from returns.context import NoDeps
4-
from returns.interfaces.failable import DiverseFailableN
4+
from returns.interfaces.failable import DiverseFailableN, SingleFailableN
55
from returns.primitives.hkt import KindN, kinded
66

77
_ValueType = TypeVar('_ValueType')
88
_ErrorType = TypeVar('_ErrorType')
99

10+
_SingleFailableKind = TypeVar('_SingleFailableKind', bound=SingleFailableN)
1011
_DiverseFailableKind = TypeVar('_DiverseFailableKind', bound=DiverseFailableN)
1112

1213

14+
@overload
15+
def internal_cond(
16+
container_type: Type[_SingleFailableKind],
17+
is_success: bool,
18+
success_value: _ValueType,
19+
) -> KindN[_SingleFailableKind, _ValueType, _ErrorType, NoDeps]:
20+
"""Reduce the boilerplate when choosing paths with ``SingleFailableN``."""
21+
22+
23+
@overload
1324
def internal_cond(
1425
container_type: Type[_DiverseFailableKind],
1526
is_success: bool,
1627
success_value: _ValueType,
1728
error_value: _ErrorType,
1829
) -> KindN[_DiverseFailableKind, _ValueType, _ErrorType, NoDeps]:
30+
"""Reduce the boilerplate when choosing paths with ``DiverseFailableN``."""
31+
32+
33+
def internal_cond( # type: ignore
34+
container_type: Union[
35+
Type[_SingleFailableKind], Type[_DiverseFailableKind],
36+
],
37+
is_success: bool,
38+
success_value: _ValueType,
39+
error_value: Optional[_ErrorType] = None,
40+
):
1941
"""
20-
Reduce the boilerplate when choosing paths with ``DiverseFailableN``.
42+
Reduce the boilerplate when choosing paths.
43+
44+
Works with ``SingleFailableN`` (e.g. ``Maybe``)
45+
and ``DiverseFailableN`` (e.g. ``Result``).
46+
47+
Example using ``cond`` with the ``Result`` container:
2148
2249
.. code:: python
2350
@@ -35,10 +62,25 @@ def internal_cond(
3562
>>> assert is_numeric('42') == Success('It is a number')
3663
>>> assert is_numeric('non numeric') == Failure('It is not a number')
3764
65+
Example using ``cond`` with the ``Maybe`` container:
66+
67+
.. code:: python
68+
69+
>>> from returns.maybe import Maybe, Some, Nothing
70+
71+
>>> def is_positive(number: int) -> Maybe[int]:
72+
... return cond(Maybe, number > 0, number)
73+
74+
>>> assert is_positive(10) == Some(10)
75+
>>> assert is_positive(-10) == Nothing
76+
3877
"""
3978
if is_success:
4079
return container_type.from_value(success_value)
41-
return container_type.from_failure(error_value)
80+
81+
if issubclass(container_type, DiverseFailableN):
82+
return container_type.from_failure(error_value)
83+
return container_type.empty # type: ignore
4284

4385

4486
#: Kinded version of :func:`~internal_cond`, use it to infer real return type.

returns/pointfree/cond.py

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,56 @@
1-
from typing import Callable, Type, TypeVar
1+
from typing import Callable, Optional, Type, TypeVar, Union, overload
22

33
from returns.context import NoDeps
4-
from returns.interfaces.failable import DiverseFailableN
4+
from returns.interfaces.failable import DiverseFailableN, SingleFailableN
55
from returns.methods.cond import internal_cond
66
from returns.primitives.hkt import Kinded, KindN, kinded
77

88
_ValueType = TypeVar('_ValueType')
99
_ErrorType = TypeVar('_ErrorType')
1010

1111
_DiverseFailableKind = TypeVar('_DiverseFailableKind', bound=DiverseFailableN)
12+
_SingleFailableKind = TypeVar('_SingleFailableKind', bound=SingleFailableN)
1213

1314

15+
@overload
16+
def cond(
17+
container_type: Type[_SingleFailableKind],
18+
success_value: _ValueType,
19+
) -> Kinded[
20+
Callable[
21+
[bool], KindN[_SingleFailableKind, _ValueType, _ErrorType, NoDeps],
22+
]
23+
]:
24+
"""Reduce the boilerplate when choosing paths with ``SingleFailableN``."""
25+
26+
27+
@overload
1428
def cond(
1529
container_type: Type[_DiverseFailableKind],
1630
success_value: _ValueType,
1731
error_value: _ErrorType,
1832
) -> Kinded[
1933
Callable[
2034
[bool], KindN[_DiverseFailableKind, _ValueType, _ErrorType, NoDeps],
21-
],
35+
]
2236
]:
37+
"""Reduce the boilerplate when choosing paths with ``DiverseFailableN``."""
38+
39+
40+
def cond( # type: ignore
41+
container_type: Union[
42+
Type[_SingleFailableKind], Type[_DiverseFailableKind],
43+
],
44+
success_value: _ValueType,
45+
error_value: Optional[_ErrorType] = None,
46+
):
2347
"""
24-
Reduce the boilerplate when choosing paths with ``DiverseFailableN``.
48+
Reduce the boilerplate when choosing paths.
49+
50+
Works with ``SingleFailableN`` (e.g. ``Maybe``)
51+
and ``DiverseFailableN`` (e.g. ``Result``).
52+
53+
Example using ``cond`` with the ``Result`` container:
2554
2655
.. code:: python
2756
@@ -31,11 +60,20 @@ def cond(
3160
>>> assert cond(Result, 'success', 'failure')(True) == Success('success')
3261
>>> assert cond(Result, 'success', 'failure')(False) == Failure('failure')
3362
63+
Example using ``cond`` with the ``Maybe`` container:
64+
65+
.. code:: python
66+
67+
>>> from returns.maybe import Maybe, Some, Nothing
68+
69+
>>> assert cond(Maybe, 10.0)(True) == Some(10.0)
70+
>>> assert cond(Maybe, 10.0)(False) == Nothing
71+
3472
"""
3573
@kinded
3674
def factory(
3775
is_success: bool,
38-
) -> KindN[_DiverseFailableKind, _ValueType, _ErrorType, NoDeps]:
76+
):
3977
return internal_cond(
4078
container_type, is_success, success_value, error_value,
4179
)

typesafety/test_methods/test_cond.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,15 @@
5252
reveal_type(cond(ReaderFutureResult, True, 1, 1.0)) # N: Revealed type is 'returns.context.requires_context_future_result.RequiresContextFutureResult*[builtins.int*, builtins.float*, Any]'
5353
5454
55+
- case: cond_maybe
56+
disable_cache: false
57+
main: |
58+
from returns.methods import cond
59+
from returns.maybe import Maybe
60+
61+
reveal_type(cond(Maybe, True, 'test')) # N: Revealed type is 'returns.maybe.Maybe*[builtins.str*]'
62+
63+
5564
- case: cond_custom_type
5665
disable_cache: false
5766
main: |

typesafety/test_pointfree/test_cond.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,15 @@
4343
reveal_type(cond(ReaderFutureResult, 1, 1.0)(True)) # N: Revealed type is 'returns.context.requires_context_future_result.RequiresContextFutureResult*[builtins.int*, builtins.float*, Any]'
4444
4545
46+
- case: cond_maybe
47+
disable_cache: false
48+
main: |
49+
from returns.pointfree import cond
50+
from returns.maybe import Maybe
51+
52+
reveal_type(cond(Maybe, True)(False)) # N: Revealed type is 'returns.maybe.Maybe[builtins.bool]'
53+
54+
4655
- case: cond_custom_type
4756
disable_cache: false
4857
main: |

0 commit comments

Comments
 (0)