forked from Alepha/Alepha
Function Composition helpers
This commit is contained in:
@ -23,6 +23,7 @@ link_libraries( alepha )
|
|||||||
# The subdirs to build
|
# The subdirs to build
|
||||||
add_subdirectory( Meta )
|
add_subdirectory( Meta )
|
||||||
add_subdirectory( Atomic )
|
add_subdirectory( Atomic )
|
||||||
|
add_subdirectory( Functional )
|
||||||
add_subdirectory( Proof )
|
add_subdirectory( Proof )
|
||||||
add_subdirectory( Memory )
|
add_subdirectory( Memory )
|
||||||
add_subdirectory( IOStreams )
|
add_subdirectory( IOStreams )
|
||||||
|
@ -214,6 +214,13 @@ namespace Alepha::Hydrogen ::detail:: Concepts_m
|
|||||||
template< typename T >
|
template< typename T >
|
||||||
concept UnaryFunction= Functional< T > and function_traits< T >::args_size == 1;
|
concept UnaryFunction= Functional< T > and function_traits< T >::args_size == 1;
|
||||||
|
|
||||||
|
template< typename T, typename Signature >
|
||||||
|
concept Function= ConvertibleTo< T, std::function< Signature > >;
|
||||||
|
|
||||||
|
template< typename T, typename ReturnType >
|
||||||
|
concept FunctionReturning= Functional< T >
|
||||||
|
and ConvertibleTo< typename function_traits< T >::return_type, ReturnType >;
|
||||||
|
|
||||||
template< typename T >
|
template< typename T >
|
||||||
concept StandardLayout= std::is_standard_layout_v< T >;
|
concept StandardLayout= std::is_standard_layout_v< T >;
|
||||||
|
|
||||||
|
1
Functional/CMakeLists.txt
Normal file
1
Functional/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
add_subdirectory( composition.test )
|
137
Functional/composition.h
Normal file
137
Functional/composition.h
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
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;
|
||||||
|
}
|
48
Functional/composition.test/0.cc
Normal file
48
Functional/composition.test/0.cc
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
static_assert( __cplusplus >= 2023'02 );
|
||||||
|
|
||||||
|
#include <Alepha/Functional/composition.h>
|
||||||
|
|
||||||
|
#include <Alepha/Testing/test.h>
|
||||||
|
|
||||||
|
#include <Alepha/Utility/enroll.h>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
using namespace Alepha::Testing::exports;
|
||||||
|
|
||||||
|
|
||||||
|
auto init= Alepha::Utility::enroll <=[]
|
||||||
|
{
|
||||||
|
"smoke test"_test <=[]( Environment test )
|
||||||
|
{
|
||||||
|
using namespace Alepha::Functional::exports::composition_m;
|
||||||
|
|
||||||
|
auto i= ident * ident;
|
||||||
|
|
||||||
|
auto add_two= []( const int x )
|
||||||
|
{
|
||||||
|
return x + 2;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto mult_two= []( const int x ) { return x * 2; };
|
||||||
|
|
||||||
|
auto add_two_then_mult_two= mult_two * add_two;
|
||||||
|
|
||||||
|
test.expect( add_two_then_mult_two( 1 ) == 6 );
|
||||||
|
};
|
||||||
|
|
||||||
|
"compose test"_test <=[]( Environment test )
|
||||||
|
{
|
||||||
|
using namespace Alepha::Functional::exports::composition_m;
|
||||||
|
|
||||||
|
auto add= []( const int x, const int y )
|
||||||
|
{
|
||||||
|
return x + y;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto mult3= []( const int x ) { return x * 3; };
|
||||||
|
|
||||||
|
test.expect( ( add >> mult3 )( 2, 1 ) == 9 );
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
1
Functional/composition.test/CMakeLists.txt
Normal file
1
Functional/composition.test/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
unit_test( 0 )
|
@ -138,6 +138,7 @@ namespace Alepha::Hydrogen::Testing
|
|||||||
};
|
};
|
||||||
|
|
||||||
using TestState= TestStateCore &;
|
using TestState= TestStateCore &;
|
||||||
|
using Environment= TestState;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline auto
|
inline auto
|
||||||
|
Reference in New Issue
Block a user