Function Composition

Function composition combines multiple functions into a single function. For example, we can compose two functions \(f(x)\) and \(g(x)\) into a new function \(h(x)\) such that \(h(x) = f(g(x))\).

calcpy can convert a Python function to a component for future function compositions. The component can be passed around, just like other callable objects.

Example: Below codes combine the components calcpy.add and calcpy.itemgetter to a composed callable combo. When the combo is called with the argument [3, 4, 5], it returns 16 since itergetter(1)([3, 4, 5]) returns 4, itemgetter(2, default=3)([3, 4, 5]) returns 5, and add([4, 7, 5]) return 16. Note that add and itemgetter are imported after the composition mode is enabled by calcpy.enable_composition(). And the component itemgetter is used twice and the two calls are executed independently.

>>> from calcpy import enable_composition
>>> enable_composition()
>>> from calcpy import add, itemgetter
>>> combo = add(itemgetter(1), 7, itemgetter(2, default=3))
>>> combo([3, 4, 5])
16

Composition Mode

The feature of function composition works only when the composition mode is enabled. The composition mode is a global setting that affects all functions in the current module.

By default, the composition mode is disabled, meaning that no function compositions are allowed.

To use the function composition feature, you need to enable the composition mode. The composition mode can be enabled by calcpy.enable_composition(), and can be disabled by calcpy.disable_composition().

The composition mode contains three aspects:

  • args (bool): whether to composite positional arguments.

  • kwargs (bool): whether to composite keyword arguments.

  • force_callable (bool): whether to force the results to be callable, even when there are no child callable. If True, the composited function will always be a callable. Otherwise, it is a callable only when one positional argument or one keyword argument is a callable.

Composition mode can be configured by the following APIs:

calcpy.set_composition_mode(args=None, kwargs=None, force_callable=None)[source]

Set composition mode.

Parameters:
  • args (bool, optional) – whether to enable composition for positional arguments. The default is None, meaning that the current status will be kept.

  • kwargs (bool, optional) – whether to enable composition for keyword arguments The default is None, meaning that the current status will be kept.

  • force_callable (bool, optional) – whether to force return results to be callable The default is None, meaning that the current status will be kept.

Examples

>>> set_composition_mode(args=False, kwargs=False, force_callable=False)
calcpy.disable_composition()[source]

Disable args composition and kwargs composition.

Alias of set_composition_mode(args=False, kwargs=False)

calcpy.enable_composition()[source]

Enable args composition and kwargs composition.

Alias of set_composition_mode(args=True, kwargs=True)

calcpy.disable_args_composition()[source]

Disable args composition.

Alias of set_composition_mode(args=False)

calcpy.enable_args_composition()[source]

Enable args composition.

Alias of set_composition_mode(args=True)

calcpy.disable_kwargs_composition()[source]

Disable kwargs composition.

Alias of set_composition_mode(kwargs=False)

calcpy.enable_kwargs_composition()[source]

Enable kwargs composition.

Alias of set_composition_mode(kwargs=True)

calcpy.disable_force_callable()[source]

Disable force callable.

Alias of set_composition_mode(force_callable=False)

calcpy.enable_force_callable()[source]

Enable force callable.

Alias of set_composition_mode(force_callable=True)

calcpy.args_composition_enabled()[source]

Return True when args composition is enabled.

Return type:

bool

calcpy.kwargs_composition_enabled()[source]

Return True when kwargs composition is enabled.

Return type:

bool

calcpy.composition_enabled()[source]

Return True when args composition or kwargs composition or force callable is enabled.

Return type:

bool

calcpy.composition_mode_context(args=None, kwargs=None, force_callable=None)[source]

Context manager to temporarily set composite mode in a with statement.

Parameters:
  • args (bool) – whether to enable composition for positional arguments

  • kwargs (bool) – whether to enable composition for keyword arguments

  • force_callable (bool) – whether to force return results to be callable

Examples

>>> force_callable_enabled()
False
>>> with composition_mode_context(args=False, kwargs=False, force_callable=True):
...     force_callable_enabled()
True
>>> force_callable_enabled()
False

Make Component

calcpy.componentize(how, /)[source]

Decorate a callable function so that it becomes a building block of function composition.

Need to enable composition mode before using this function.

Parameters:

how – Callable object that combines multiple results.

Return type:

callable

Examples

>>> from calcpy import itemgetter
>>> enable_composition()
>>> max_ = componentize(max)
>>> max_(itemgetter(1), 7, itemgetter(2, default=3))([3, 4, 5])
7
>>> min_ = componentize(min)
>>> min_(itemgetter(1), 7, itemgetter(2, default=3))([3, 4, 5])
4
>>> min_(3, 4, 5)
3
>>> disable_composition()

Shorthand Functions that Support Composition

You need to enable the composition mode before import the shorthand functions. If those functions are imported before the composition mode is enabled, they will not be composable.

calcpy.abs_(): adapted from operator.abs().

calcpy.acos(): adapted from math.acos().

calcpy.acosh(): adapted from math.acosh().

calcpy.add()

calcpy.all_()

calcpy.and_()

calcpy.any_()

calcpy.asin(): adapted from math.asin().

calcpy.asinh(): adapted from math.asinh().

calcpy.atan(): adapted from math.atan().

calcpy.atan2(): adapted from math.atan2().

calcpy.bin_(): adapted from bin().

calcpy.bool_(): adapted from bool().

calcpy.ceil(): adapted from math.ceil().

calcpy.comb(): adapted from math.comb().

calcpy.concat()

calcpy.contains(): adapted from operator.contains().

calcpy.cos(): adapted from math.cos().

calcpy.cosh(): adapted from math.cosh().

calcpy.countOf(): adapted from operator.countOf().

calcpy.count_unique()

calcpy.crbt()

calcpy.cycleperm()

calcpy.degrees(): adapted from math.degrees().

calcpy.dict_(): adapted from dict().

calcpy.difference()

calcpy.distinct()

calcpy.dist(): adapted from math.dist().

calcpy.divmod_(): adapted from divmod().

calcpy.eq()

calcpy.erf(): adapted from math.erf().

calcpy.erfc(): adapted from math.erfc().

calcpy.exp(): adapted from math.exp().

calcpy.exp2(): adapted from math.exp2().

calcpy.expm1(): adapted from math.expm1().

calcpy.factorial(): adapted from math.factorial().

calcpy.fillnan()

calcpy.fillnone()

calcpy.float_(): adapted from float().

calcpy.floor(): adapted from math.floor().

calcpy.floordiv(): adapted from operator.floordiv().

calcpy.fma()

calcpy.format_(): adapted from format().

calcpy.gamma(): adapted from math.gamma().

calcpy.gcd()

calcpy.ge()

calcpy.getitem(): adapted from operator.getitem().

calcpy.gt()

calcpy.hex_(): adapted from hex().

calcpy.index(): adapted from operator.index()..

calcpy.indexOf(): adapted from operator.indexOf().

calcpy.int_(): adapted from int().

calcpy.intersection()

calcpy.inv(): adapted from operator.inv().

calcpy.is_(): adapted from operator.is_().

calcpy.is_not(): adapted from operator.is_not().

calcpy.isclose(): adapted from math.isclose().

calcpy.isdisjoint()

calcpy.isfinite(): adapted from math.isfinite().

calcpy.isinf(): adapted from math.isinf().

calcpy.isinstance_(): adapted from isinstance().

calcpy.ispropersubset()

calcpy.ispropersuperset()

calcpy.isqrt(): adapted from math.isqrt().

calcpy.issubset()

calcpy.issuperset()

calcpy.lcm()

calcpy.le()

calcpy.len_(): adapted from len().

calcpy.lgamma(): adapted from math.lgamma().

calcpy.list_(): adapted from list().

calcpy.log(): adapted from math.log().

calcpy.log2(): adapted from math.log2().

calcpy.log10(): adapted from math.log10().

calcpy.log1p(): adapted from math.log1p().

calcpy.lshift(): adapted from operator.lshift().

calcpy.lt()

calcpy.matmul()

calcpy.matprod()

calcpy.max_(): adapted from max().

calcpy.maximal()

calcpy.min_(): adapted from min().

calcpy.min_repetend_len()

calcpy.minimial()

calcpy.mod(): adapted from operator.mod().

calcpy.mul()

calcpy.ne()

calcpy.neg(): adapted from operator.neg().

calcpy.never()

calcpy.not_(): adapted from operator.not_().

calcpy.oct_(): adapted from oct().

calcpy.odd()

calcpy.or_()

calcpy.ord_(): adapted from ord().

calcpy.perm()

calcpy.pos(): adapted from operator.pos().

calcpy.pow_(): adapted from operator.pow().

calcpy.prioritize()

calcpy.prod(): adapted from math.prod().

calcpy.radians(): adapted from math.radians().

calcpy.range_(): adapted from range().

calcpy.remainder(): adapted from math.remainder().

calcpy.round_(): adapted from round().

calcpy.rshift(): adapted from operator.rshift().

calcpy.same()

calcpy.set_(): adapted from set().

calcpy.setitem(): adapted from operator.setitem().

calcpy.sorted_(): adapted from sorted().

calcpy.sqrt(): adapted from math.sqrt().

calcpy.str_(): adapted from str().

calcpy.sub(): adapted from operator.sub().

calcpy.sum_(): adapted from sum().

calcpy.sumprod()

calcpy.swap()

calcpy.symmetric_difference()

calcpy.tan(): adapted from math.tan().

calcpy.tanh(): adapted from math.tanh().

calcpy.truediv(): adapted from operator.truediv().

calcpy.truth(): adapted from operator.truth().

calcpy.tuple_(): adapted from tuple().

calcpy.type_(): adapted from type().

calcpy.union()

calcpy.unique()

calcpy.xor()

calcpy.zip_(): adapted from zip().

Chained Composition

calcpy.fun.skewer(*callables)[source]

Composite multiple callables into one callable.

Parameters:

*callables (callable)

Returns:

A callable that calls all callables in order.

Return type:

callable

Examples

>>> def minmax(x, y):
...     return min(x, y), max(x, y)
>>> def mul(x, y):
...     return x * y
>>> skewered = skewer(minmax, mul)
>>> skewered(5, 3)
15
calcpy.fun.repeat(n=1)[source]

Repeat a callable n times.

Parameters:

n (int) – Number of times to repeat.

Returns:

a callable that swaps a callable so that it is called n times.

Return type:

Callable[callable, callable]

Examples

Apply on a function.

>>> repeated = repeat(n=2)(abs)
>>> repeated(-3)
3

Use as a decorator.

>>> @repeat(n=2)
... def g(a):
...     return a + 1
>>> g(1)
3