1
0
forked from Alepha/Alepha
Files
Alepha/Functional/composition.h

138 lines
3.6 KiB
C++

static_assert( __cplusplus >= 2023'02 );
#pragma once
#include <Alepha/Alepha.h>
#include <tuple>
#include <Alepha/Concepts.h>
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;
}