feat: functional composition of functors
NEEDS: Moore!1497 (merged) Analysis!881 (merged) DaVinci!687 (merged)
This is a followup to the recent discussions in !2666 (closed) and !2672 (closed)
NOTE This is NOT completely ready yet. But before I extend this to e.g. handle ranges, extend documentation and tests I want some input.
(So please focus on the main concept, not so much on small C++ details
We are currently seeing a few implementations of Functors which, IMHO, are trying to do too much inside a single functor.
I suspect this is a bit of a symptom of the fact that functors cannot be composed in the functional sense, think functor f and g and input x, we want f(g(x)).
Please see this discussion for an example of a problematic case.
Brief version repeated here:
Instead of:
DTF_mass = F.MAP_INPUT(Functor=F.MASS, Relations="Path/To/P2P_Relations")
which hides a lot of things internally, for example implicitly taking the first entry of the returned relations range...
I think we should do:
DTF_mass = F.MASS @ F.FRONT @ F.RESOVLE_RELATIONS(Relations = "Path/To/P2P_Relations")
The composition with @
IMHO lets us easily express many things by having a set of small functors that each only do one thing.
This should be easier to read, maintain, and understand compared to having more complex functors like MAP_INPUT
This MR currently implements the foundation of making Functor @ Functor
work in python and c++.
Please see an example of this being used in the new test test_functor_compose.py
For simplicity, the important part of the output of running that test looks like this:
Gaudi::Examples... INFO executing IntDataProducer, storing 5 into SomeInt
SFA1 INFO 'Functor_Property':"('compose( compose( ::Functors::Examples::TimesTwo{}, ::Functors::Examples::TimesTwo{} ), ::Functors::Examples::PlusN( /* value to add to input */ std::integral_constant<int, 5>{} ) )', ['Functors/Example.h'], '( ( TimesTwo @ TimesTwo ) @ PlusN(n=5) )')"
SFA1 INFO Result: 40
SFA2 INFO 'Functor_Property':"('compose( compose( ::Functors::Examples::TimesTwo{}, ::Functors::Examples::PlusN( /* value to add to input */ std::integral_constant<int, 5>{} ) ), ::Functors::Examples::TimesTwo{} )', ['Functors/Example.h'], '( ( TimesTwo @ PlusN(n=5) ) @ TimesTwo )')"
SFA2 INFO Result: 30
I know this is a bit different from the current "Adapter" way, but I think this proposal might be more scalable.
IMHO this would be the best way to achieve a framework of small and easy to understand functors which are easy to use to compose more complex things with, while not hiding anything implicitly
But let's get the discussion started and see what people prefer