static_assert( __cplusplus >= 2023'02 ); #pragma once #include #include #include namespace Alepha::Hydrogen::Functional ::detail:: composition_m { inline namespace exports { constexpr struct { template< typename T > constexpr T operator() ( T t ) const { return t; } } ident; /*! * Functional Composition utilities. * * All utilities herein use value/copy semantics. This is both because * it makes it easier to implement, and because functional programming typically * eschews reference and move semantics. */ } template< typename Outer, typename Inner, typename ReturnType, typename TupleOfArguments > struct Composed; template< typename Outer, typename Inner, typename ReturnType, typename ... Args > struct Composed< Outer, Inner, ReturnType, std::tuple< Args... > > { using outer_type= Outer; using inner_type= Inner; using return_type= ReturnType; Outer outer; Inner inner; return_type operator() ( Args ... args ) { return outer( inner( std::forward< Args >( args )... ) ); } }; template< typename Intermediate, typename ReturnType > auto compose( Concepts::Function< ReturnType ( Intermediate ) > auto outer, Concepts::FunctionReturning< Intermediate > auto inner ) { using inner_traits= function_traits< decltype( inner ) >; using params= typename inner_traits::args_type; return Composed< decltype( outer ), decltype( inner ), ReturnType, params >{ std::move( outer ), std::move( inner ) }; } namespace exports { inline auto operator * ( decltype( ident ), decltype( ident ) ) { return ident; } inline auto operator * ( decltype( ident ), Concepts::Functional auto inner ) { using Intermediate= typename function_traits< decltype( inner ) >::return_type; return []( Intermediate intermediate ) { return intermediate; } * inner; } /*! * Function composition operator. * * This is a C++ notation equivalent to the `∘` operator in mathematics * for functions. Just as `f( g( x ) )` is `( f ∘ g )( x )`, the C++ * equivalent notation is `( f * g )( x )`. */ inline auto operator * ( Concepts::UnaryFunction auto outer, Concepts::Functional auto inner ) { using Intermediate= typename function_traits< decltype( inner ) >::return_type; using ReturnType= typename function_traits< decltype( inner ) >::return_type; return compose< Intermediate, ReturnType >( std::move( outer ), std::move( inner ) ); } inline auto operator >> ( decltype( ident ), decltype( ident ) ) { return ident; } inline auto operator >> ( Concepts::Functional auto first, decltype( ident ) ) { return ident * first; } /*! * Ordered function composition operator. * * While `(f * g * h)( x )` preserves the order in the * notation `f( g( h( x ) ) )`, sometimes it's desirable to * write the composition in "first, then" style syntax. This * example would be `h`, then `g`, then `f`. For this purpose, * `operator >>` has been overloaded bewteen two functions to * permit simple examples such as: * * `( first >> second >> third )( arg )` * * This operator uses the same underpinnings as the `operator *`, * and thus both syntaxes (and captures of their combinations) can * be mixed and matched. */ inline auto operator >> ( Concepts::Functional auto first, Concepts::Functional auto then ) { return then * first; } } } namespace Alepha::Hydrogen::Functional::inline exports::inline composition_m { using namespace detail::composition_m::exports; }