Skip to content

feat: functional composition of functors

Christoph Hasse requested to merge chasse_functor_compose into master

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 😊 cc'ing the functor people I know please feel free to add if I'm missing anyone: @amathad @dfazzini @graven @gunther @mvesteri @nnolte @poluekt

Edited by Christoph Hasse

Merge request reports