from typing import Any, Callable, Iterable, Optional, TypeVar, overload
_ValueType = TypeVar('_ValueType')
_NewValueType = TypeVar('_NewValueType')
_AccValueType = TypeVar('_AccValueType')
[docs]class Reduced(object):
"""
Sentinel for early termination inside transducer.
.. code:: python
>>> from returns.transducers import tmap, transduce, Reduced
>>> def add_one(number: int) -> int:
... return number + 1
>>> def add(acc: int, number: int) -> int:
... if acc == 3:
... return Reduced(acc)
... return acc + number
>>> my_list = [0, 1, 2]
>>> assert transduce(tmap(add_one), add, 0, my_list) == 3
"""
_inner_value: Any
def __init__(self, inner_value: Any) -> None:
self._inner_value = inner_value
@property
def value(self) -> Any:
return self._inner_value
class _Missing(object):
"""Represents a missing value for reducers."""
_instance: Optional['_Missing'] = None
def __new__(cls, *args: Any, **kwargs: Any) -> '_Missing':
if cls._instance is None:
cls._instance = object.__new__(cls) # noqa: WPS609
return cls._instance
#: A singleton representing any missing value
Missing = _Missing()
[docs]def transduce(
xform: Callable[
[Callable[[_AccValueType, _ValueType], _AccValueType]],
Callable[[_AccValueType, _ValueType], _AccValueType],
],
reducing_function: Callable[[_AccValueType, _ValueType], _AccValueType],
initial: _AccValueType,
iterable: Iterable[_ValueType],
) -> _AccValueType:
"""
Process information with transducers.
.. code:: python
>>> from returns.transducers import tmap, transduce
>>> def add_one(number: int) -> int:
... return number + 1
>>> def add(acc: int, number: int) -> int:
... return acc + number
>>> my_list = [0, 1, 2]
>>> assert transduce(tmap(add_one), add, 0, my_list) == 6
"""
reducer = xform(reducing_function)
return reduce(reducer, iterable, initial)
@overload
def reduce(
function: Callable[[_ValueType, _ValueType], _ValueType],
iterable: Iterable[_ValueType],
initial: _Missing = Missing,
) -> _ValueType:
"""Reduce without an initial value."""
@overload
def reduce(
function: Callable[[_AccValueType, _ValueType], _AccValueType],
iterable: Iterable[_ValueType],
initial: _AccValueType,
) -> _AccValueType:
"""Reduce with an initial value."""
[docs]def reduce(function, iterable, initial=Missing):
"""
A rewritten version of :func:`reduce <functools.reduce>`.
This version considers some features borrowed from Clojure:
- Early termination
- Function initializer [TODO]
You can use it as a normal reduce if you want:
.. code:: python
>>> from returns.transducers import reduce
>>> def add(acc: int, value: int) -> int:
... return acc + value
>>> assert reduce(add, [1, 2, 3]) == 6
"""
it = iter(iterable)
if initial is Missing:
try:
acc_value = next(it)
except StopIteration:
raise TypeError(
'reduce() of empty iterable with no initial value',
) from None
else:
acc_value = initial
for value in it: # noqa: WPS110
acc_value = function(acc_value, value)
if isinstance(acc_value, Reduced):
return acc_value.value
return acc_value