import functools as _functools
import inspect as _inspect
import itertools as _itertools
import types as _types
import typing as _t
import typing_extensions as _te
from ._core import functional as _functional
from ._core.signatures import to_signature as _to_signature
_Arg = _t.TypeVar('_Arg')
_KwArg = _t.TypeVar('_KwArg')
_Params = _te.ParamSpec('_Params')
_Result = _t.TypeVar('_Result')
_T = _t.TypeVar('_T')
_T2 = _t.TypeVar('_T2')
_T3 = _t.TypeVar('_T3')
[docs]def identity(_value: _T) -> _T:
"""
Returns object itself.
>>> identity(0)
0
"""
return _value
[docs]def cleave(
*functions: _t.Callable[..., _T]
) -> _t.Callable[..., _t.Iterable[_T]]:
"""
Returns function that separately applies
given functions to the same arguments.
>>> to_min_and_max = cleave(min, max)
>>> list(to_min_and_max(range(10)))
[0, 9]
>>> list(to_min_and_max(range(0), default=None))
[None, None]
"""
caller_frame_info = _inspect.stack()[1]
return _functional.Cleavage(*functions,
file_path=caller_frame_info.filename,
line_number=caller_frame_info.lineno,
line_offset=0)
[docs]def combine(
*maps: _t.Callable[[_T], _T2]
) -> _t.Callable[..., _t.Tuple[_T2, ...]]:
"""
Returns function that applies each map to corresponding argument.
>>> encoder_decoder = combine(str.encode, bytes.decode)
>>> encoder_decoder('hello', b'world')
(b'hello', 'world')
"""
caller_frame_info = _inspect.stack()[1]
return _functional.Combination(*maps,
file_path=caller_frame_info.filename,
line_number=caller_frame_info.lineno,
line_offset=0)
[docs]def compose(
_last_function: _t.Callable[[_T2], _T3],
_penult_function: _t.Callable[..., _T2],
*_rest_functions: _t.Callable[..., _t.Any]
) -> _t.Callable[_Params, _T3]:
"""
Returns functions composition.
>>> sum_of_first_n_natural_numbers = compose(sum, range)
>>> sum_of_first_n_natural_numbers(10)
45
"""
caller_frame_info = _inspect.stack()[1]
return _t.cast(
_t.Callable[_Params, _T3],
_functional.Composition(_last_function, _penult_function,
*_rest_functions,
file_path=caller_frame_info.filename,
line_number=caller_frame_info.lineno,
line_offset=0)
)
[docs]def curry(
_function: _t.Callable[..., _T2]
) -> _functional.Curry[_Arg, _KwArg, _T2]:
"""
Returns curried version of given function.
>>> curried_pow = curry(pow)
>>> two_to_power = curried_pow(2)
>>> two_to_power(10)
1024
"""
return _functional.Curry(_function, _to_signature(_function))
[docs]def pack(_function: _t.Callable[_Params, _T2]) -> _t.Callable[[_T, _T2], _T2]:
"""
Returns function that works with single iterable parameter
by unpacking elements to given function.
>>> packed_int = pack(int)
>>> packed_int(['10'])
10
>>> packed_int(['10'], {'base': 2})
2
"""
return _functools.partial(call, _function)
[docs]def call(_function: _t.Callable[_Params, _T2],
_args: _Params.args,
_kwargs: _Params.kwargs = _types.MappingProxyType({})) -> _T2:
"""
Calls given function with given positional and keyword arguments.
"""
return _function(*_args, **_kwargs)
[docs]def to_constant(_value: _T) -> _t.Callable[..., _T]:
"""
Returns function that always returns given value.
>>> always_zero = to_constant(0)
>>> always_zero()
0
>>> always_zero(1)
0
>>> always_zero(how_about=2)
0
"""
return _functional.Constant(_value)
[docs]def flip(_function: _t.Callable[..., _T2]) -> _t.Callable[..., _T2]:
"""
Returns function with positional arguments flipped.
>>> flipped_power = flip(pow)
>>> flipped_power(2, 4)
16
"""
return _t.cast(_t.Callable[..., _T2],
_functional.Flip.from_function(_function))
[docs]def flatmap(_function: _t.Callable[[_T], _t.Iterable[_T2]],
*iterables: _t.Iterable[_T]) -> _t.Iterable[_T2]:
"""
Applies given function to the arguments aggregated from given iterables
and concatenates results into plain iterable.
>>> list(flatmap(range, range(5)))
[0, 0, 1, 0, 1, 2, 0, 1, 2, 3]
"""
yield from _itertools.chain.from_iterable(map(_function, *iterables))