FunctionChains.jl

FunctionChains.jl provides chained/composed functions with functionality beyond Base.ComposedFunction, as well as some other ways of combining functions.

The package defines the function fchain that turns a sequence of functions into a callable function chain. Functions are applied in the order they are given, so fchain(f, g, h) and fchain((f, g, h)) are semantically equivalent to h ∘ g ∘ f.

Function chains can be based on tuples of functions but also on arrays (e.g. fchain([x -> i * x for i in 1:4])) or generators/iterators (e.g. fchain((x -> i * x for i in 1:4))) of functions. fchain will always try to generate type-stable function chains if possible.

The function chain generated by fchain supports InverseFunctions.inverse and/or ChangesOfVariables.with_logabsdet_jacobian, as well as the Adapt, Functors and FlexiMaps APIs, if all functions in the chain do support the respective API.

frepeat(f, n) and f∘̂ n build a chain of n repetitions of function f. Use fcomp(f, g, h) to compose functions in the order f ∘ g ∘ h, equivalent to fchain(h, g, f).

fcprod(fs) constructs a Cartesian product of functions.

ffanout(fs) constructs a function fanout.

FunctionChains also exports the convenience functions asfunction(f) and typed_callable(f) that wrap a callable f in a properly typed callable function (object) if necessary, e.g. if f is a type (constructor).

Function chains

Example:

using FunctionChains

f = fchain(Complex, sqrt)
f(-1)

# output

0.0 + 1.0im
g = fchain((x -> i * x for i in 1:4))
g(1.75)

# output

42.0

The functions chains f and g in this example are both type stable

using Test
@inferred f(-1)
@inferred g(1.75)

In general, type stability will depend on the functions in the sequence and the type of the sequence itself.

Function products

A Cartesian product of functions applies some kind of collection (a tuple, array, NamedTuple or an iterable in general) of functions to a collection (with the same shape) of values, in an component/element-wise fashion:

fp = fcprod(x -> 2*x, x -> 3*x)
fp((4, 5))

# output

(8, 15)
fp = fcprod((a = x -> 2*x, b = x -> 3*x))
fp((a = 4, b = 5))

# output

(a = 8, b = 15)

Function fanouts

A function fanout applies some kind of collection a functions to single input, separately, resulting in a collection of the outputs:

fp = ffanout([Base.Fix1(*, 2), Base.Fix1(*, 3), Base.Fix1(*, 4)])
fp(4) == [8, 12, 16]

# output

true
fp = ffanout((a = x -> 2*x, b = x -> 3*x))
fp(4)

# output

(a = 8, b = 12)