clean-up: simplify function-closure -- can now remove obsoleted impl

A lot of repetitive, pre C++11 metaprogramming code can now be removed,
and several helper constructs, which were needed to handle generic
function application and passing a tuple of values to create a binder.

Note however, the highly complex and technical core of this header
still remains intact; which is to create a ''partial closure'' over
some arguments of a function, while keeping the remaining arguments
open as parameters for invocation.

TODO: Even in the remaining code there is a lot of redundancy
and helper construct which are no longer necessary
This commit is contained in:
Fischlurch 2025-06-05 19:11:46 +02:00
parent 738d9e5b67
commit f6f8220fbe
5 changed files with 52 additions and 449 deletions

View file

@ -33,12 +33,6 @@
** @todo the implementation is able to handle partial application with N arguments, ** @todo the implementation is able to handle partial application with N arguments,
** but currently we need just one argument, thus only this case was wrapped ** but currently we need just one argument, thus only this case was wrapped
** up into a convenient functions func::applyFirst and func::applyLast ** up into a convenient functions func::applyFirst and func::applyLast
** @todo 11/23 these functor-utils were written at a time when support for handling
** generic functions in C++ was woefully inadequate; at that time, we neither
** had Lambda-support in the language, nor the ability to use variadic arguments.
** Providing a one-shot function-style interface for this kind of manipulations
** is still considered beneficial, and thus we should gradually modernise
** the tools we want to retain...
** @todo 2/25 note that there is a bind_front in C++20 and C++23 will provide a bind_back ** @todo 2/25 note that there is a bind_front in C++20 and C++23 will provide a bind_back
** helper, which would provide the implementation fully in accordance with current ** helper, which would provide the implementation fully in accordance with current
** expectations (including move, constexpr); if we choose to retain a generic ** expectations (including move, constexpr); if we choose to retain a generic
@ -83,191 +77,6 @@ namespace func{
namespace { // helpers for binding and applying a function to an argument tuple namespace { // helpers for binding and applying a function to an argument tuple
using std::get;
/**
* this Helper with repetitive specialisations for up to nine arguments
* is used either to apply a function to arguments given as a tuple, or
* to create the actual closure (functor) over all function arguments.
* @todo 2/2025 should be replaced by a single variadic template, and
* implemented using std::apply. Note also that std::bind would
* support perfect-forwarding, especially also for the functor;
* the latter would allow to use move-only functors.
*/
template<uint n>
struct Apply;
template<> //__________________________________
struct Apply<0> ///< Apply function without Arguments
{
template<typename RET, class FUN, class TUP>
static RET
bind (FUN& f, TUP&)
{
return std::bind (f);
}
};
template<> //_________________________________
struct Apply<1> ///< Apply function with 1 Argument
{
template<typename RET, class FUN, class TUP>
static RET
bind (FUN& f, TUP & arg)
{
return std::bind (f, get<0>(arg));
}
};
template<> //_________________________________
struct Apply<2> ///< Apply function with 2 Arguments
{
template<typename RET, class FUN, class TUP>
static RET
bind (FUN& f, TUP & arg)
{
return std::bind (f, get<0>(arg)
, get<1>(arg)
);
}
};
template<> //_________________________________
struct Apply<3> ///< Apply function with 3 Arguments
{
template<typename RET, class FUN, class TUP>
static RET
bind (FUN& f, TUP & arg)
{
return std::bind (f, get<0>(arg)
, get<1>(arg)
, get<2>(arg)
);
}
};
template<> //_________________________________
struct Apply<4> ///< Apply function with 4 Arguments
{
template<typename RET, class FUN, class TUP>
static RET
bind (FUN& f, TUP & arg)
{
return std::bind (f, get<0>(arg)
, get<1>(arg)
, get<2>(arg)
, get<3>(arg)
);
}
};
template<> //_________________________________
struct Apply<5> ///< Apply function with 5 Arguments
{
template<typename RET, class FUN, class TUP>
static RET
bind (FUN& f, TUP & arg)
{
return std::bind (f, get<0>(arg)
, get<1>(arg)
, get<2>(arg)
, get<3>(arg)
, get<4>(arg)
);
}
};
template<> //_________________________________
struct Apply<6> ///< Apply function with 6 Arguments
{
template<typename RET, class FUN, class TUP>
static RET
bind (FUN& f, TUP & arg)
{
return std::bind (f, get<0>(arg)
, get<1>(arg)
, get<2>(arg)
, get<3>(arg)
, get<4>(arg)
, get<5>(arg)
);
}
};
template<> //_________________________________
struct Apply<7> ///< Apply function with 7 Arguments
{
template<typename RET, class FUN, class TUP>
static RET
bind (FUN& f, TUP & arg)
{
return std::bind (f, get<0>(arg)
, get<1>(arg)
, get<2>(arg)
, get<3>(arg)
, get<4>(arg)
, get<5>(arg)
, get<6>(arg)
);
}
};
template<> //_________________________________
struct Apply<8> ///< Apply function with 8 Arguments
{
template<typename RET, class FUN, class TUP>
static RET
bind (FUN& f, TUP & arg)
{
return std::bind (f, get<0>(arg)
, get<1>(arg)
, get<2>(arg)
, get<3>(arg)
, get<4>(arg)
, get<5>(arg)
, get<6>(arg)
, get<7>(arg)
);
}
};
template<> //_________________________________
struct Apply<9> ///< Apply function with 9 Arguments
{
template<typename RET, class FUN, class TUP>
static RET
bind (FUN& f, TUP & arg)
{
return std::bind (f, get<0>(arg)
, get<1>(arg)
, get<2>(arg)
, get<3>(arg)
, get<4>(arg)
, get<5>(arg)
, get<6>(arg)
, get<7>(arg)
, get<8>(arg)
);
}
};
/* ===== Helpers for partial function application ===== */
/** @note relying on the implementation type /** @note relying on the implementation type
* since we need to _build_ placeholders */ * since we need to _build_ placeholders */
using std::_Placeholder; using std::_Placeholder;
@ -371,47 +180,23 @@ namespace func{
/* ======= core operations: closures and partial application ========= */ /* ======= core operations: closures and partial application ========= */
/** /**
* Closure-creating template. * Base technique: build a binder with arguments from a tuple.
* The instance is linked (reference) to a given concrete argument tuple. * @remark based on the apply-λ-trick by David Vandervoorde
* A functor with a matching signature may then either be _closed_ over * to unpack the tuple elements as argument pack
* this argument values, or even be invoked right away with theses arguments. * @note both the functor and the arguments can be passed
* @warning we take functor objects _and parameters_ by reference * by _perfect forwarding_, yet both will be **copied**
* into the resulting binder
* @param fun anything »invocable«
* @param tuple a suitable set of arguments, or std binding placeholders
* @return a _binder functor_, as generated by `std::bind`; all arguments
* supplied with explicitly given values will be _closed_, while
* argument positions marked with `std::_Placeholder<N>` instances
* will remain _open_ to accept arguments on the resulting function.
*/ */
template<typename SIG>
class TupleApplicator
{
using Args = typename _Fun<SIG>::Args;
using Ret = typename _Fun<SIG>::Ret;
using BoundFunc = function<Ret()>;
enum { ARG_CNT = count<typename Args::List>() };
/** storing a ref to the parameter tuple */
Tuple<Args>& params_;
public:
TupleApplicator (Tuple<Args>& args)
: params_(args)
{ }
BoundFunc bind (SIG& f) { return Apply<ARG_CNT>::template bind<BoundFunc> (f, params_); }
BoundFunc bind (function<SIG> const& f) { return Apply<ARG_CNT>::template bind<BoundFunc> (f, params_); }
template<class FUN>
Ret
operator() (FUN&& f)
{
ASSERT_VALID_SIGNATURE (FUN,SIG);
return std::apply (forward<FUN> (f), params_);
}
};
template<class FUN, class TUP, typename = enable_if_Tuple<TUP>> template<class FUN, class TUP, typename = enable_if_Tuple<TUP>>
auto auto
bindArgTuple (FUN&& fun, TUP&& tuple) bindArgTuple (FUN&& fun, TUP&& tuple)
@ -427,37 +212,6 @@ namespace func{
/**
* Closing a function over its arguments.
* This is a small usage example or spin-off,
* having almost the same effect than invoking `std::bind()`.
* The notable difference is that the function arguments for
* creating the closure are passed in as one tuple compound.
*/
template<typename SIG>
class FunctionClosure
{
typedef typename _Fun<SIG>::Args Args;
typedef typename _Fun<SIG>::Ret Ret;
function<Ret(void)> closure_;
public:
FunctionClosure (SIG& f, Tuple<Args>& args)
: closure_{bindArgTuple (f, args)}
{ }
FunctionClosure (function<SIG> const& f, Tuple<Args>& args)
: closure_{bindArgTuple (f, args)}
{ }
Ret operator() () { return closure_(); }
typedef Ret result_type; ///< for STL use
typedef void argument_type;
};
/** /**
* Partial function application * Partial function application
@ -638,14 +392,6 @@ namespace func{
using util::unConst; using util::unConst;
template<typename SIG, typename ARG>
struct _Clo
{
using Ret = typename _Fun<SIG>::Ret;
using Signature = typename BuildFunType<Ret, ARG>::Sig;
using Type = FunctionClosure<Signature>;
};
template<typename FUN1, typename FUN2> template<typename FUN1, typename FUN2>
struct _Chain struct _Chain
{ {
@ -744,21 +490,6 @@ namespace func{
/* ========== function-style interface ============= */ /* ========== function-style interface ============= */
/** close the given function over all arguments,
* using the values from the argument tuple.
* @return a closure object, which can be
* invoked later to yield the
* function result. */
template<typename SIG, typename...ARG>
inline
typename _Clo<SIG,TySeq<ARG...>>::Type
closure (SIG& f, std::tuple<ARG...>& args)
{
using Closure = typename _Clo<SIG,TySeq<ARG...>>::Type;
return Closure (f,args);
}
/** close the given function over the first argument. /** close the given function over the first argument.
* @warning never tie an ownership-managing object by-value! */ * @warning never tie an ownership-managing object by-value! */
template<typename FUN, typename ARG> template<typename FUN, typename ARG>

View file

@ -109,13 +109,11 @@ out-lit: List2 :-<5>-<6>-<7>-
out-lit: Args :-<5>-<9>- out-lit: Args :-<5>-<9>-
out-lit: NewArgs :-<1>-<5>-<9>- out-lit: NewArgs :-<1>-<5>-<9>-
out-lit: : out-lit: :
out-lit: : ---Apply--- out-lit: : ---Bind----
out-lit: tup0 :«tuple<>»──() out-lit: tup0 :«tuple<>»──()
out-lit: tup1 :«tuple<int>»──(11) out-lit: tup1 :«tuple<int>»──(11)
out-lit: tup2 :«tuple<int, int>»──(11,12) out-lit: tup2 :«tuple<int, int>»──(11,12)
out-lit: tup3 :«tuple<int, int, int>»──(11,12,13) out-lit: tup3 :«tuple<int, int, int>»──(11,12,13)
out-lit: :
out-lit: : ---Bind----
return: 0 return: 0
END END

View file

@ -50,16 +50,8 @@ namespace test {
namespace { // test data namespace { // test data
using List1 = TySeq<Num<1>, Num<2>, Num<3> >::List;
using List2 = TySeq<Num<5>, Num<6>, Num<7> >::List;
typedef TySeq< Num<1>
, Num<2>
, Num<3>
>::List List1;
typedef TySeq< Num<5>
, Num<6>
, Num<7>
>::List List2;
/** special test fun /** special test fun
@ -82,24 +74,24 @@ namespace test {
using func::Apply;
using func::TupleApplicator;
using func::bindArgTuple; using func::bindArgTuple;
using func::FunctionClosure;
using func::closure;
/*********************************************************************//** /*********************************************************************//**
* @test building a function closure for a given function or functor, * @test building a function closure for a given function or functor,
* while arguments are passed in as tuple * while arguments are passed in as tuple
* - accessing signatures as typelists * - accessing signatures as typelists
* - apply free function to tuple
* - apply functor to tuple
* - bind free function to tuple * - bind free function to tuple
* - bind functor to tuple * - bind functor to tuple
* - build a simple "tuple closure"
* @remark this test is _rather low-level_ and documents the construction * @remark this test is _rather low-level_ and documents the construction
* of the implementation * of the implementation; furthermore, most of this construction
* was obsoleted by newer language features, notably std::apply
* and the technique to unpack variadic-λ arguments.
* What remains, is now largely a definition how to handle
* function argument list signatures, to build suitable
* argument tuple types by metaprogramming, and finally
* to pass them to construct a binder.
* @see function-composition-test.cpp (advanced features like partial application)
*/ */
class FunctionClosure_test : public Test class FunctionClosure_test : public Test
{ {
@ -108,11 +100,8 @@ namespace test {
{ {
verify_setup(); verify_setup();
check_signatureTypeManip(); check_signatureTypeManip();
check_applyFree();
check_applyFunc();
check_bindFree(); check_bindFree();
check_bindFunc(); check_bindFunc();
build_closure();
} }
@ -150,56 +139,6 @@ namespace test {
} }
void
check_applyFree ()
{
cout << "\t:\n\t: ---Apply---\n";
Tuple<TySeq<>> tup0 ;
Tuple<TySeq<int>> tup1 (11);
Tuple<TySeq<int,int>> tup2 (11,12);
Tuple<TySeq<int,int,int>> tup3 (11,12,13);
DUMPVAL (tup0);
DUMPVAL (tup1);
DUMPVAL (tup2);
DUMPVAL (tup3);
CHECK (-1 == TupleApplicator<int()> (tup0) (fun0) );
CHECK (11 == TupleApplicator<int(int)> (tup1) (fun1) );
CHECK (11+12 == TupleApplicator<int(int,int)> (tup2) (fun2) );
CHECK (11+12+13 == TupleApplicator<int(int,int,int)> (tup3) (fun3) );
CHECK (-1 == std::apply(fun0, tup0) );
CHECK (11 == std::apply(fun1, tup1) );
CHECK (11+12 == std::apply(fun2, tup2) );
CHECK (11+12+13 == std::apply(fun3, tup3) );
}
void
check_applyFunc ()
{
Tuple<TySeq<>> tup0 ;
Tuple<TySeq<int>> tup1 (11);
Tuple<TySeq<int,int>> tup2 (11,12);
Tuple<TySeq<int,int,int>> tup3 (11,12,13);
function<int()> functor0 (fun0);
function<int(int)> functor1 (fun1);
function<int(int,int)> functor2 (fun2);
function<int(int,int,int)> functor3 (fun3);
CHECK (-1 == TupleApplicator<int()> (tup0) (functor0) );
CHECK (11 == TupleApplicator<int(int)> (tup1) (functor1) );
CHECK (11+12 == TupleApplicator<int(int,int)> (tup2) (functor2) );
CHECK (11+12+13 == TupleApplicator<int(int,int,int)> (tup3) (functor3) );
CHECK (-1 == std::apply(functor0, tup0) );
CHECK (11 == std::apply(functor1, tup1) );
CHECK (11+12 == std::apply(functor2, tup2) );
CHECK (11+12+13 == std::apply(functor3, tup3) );
}
void void
check_bindFree () check_bindFree ()
{ {
@ -214,22 +153,12 @@ namespace test {
DUMPVAL (tup2); DUMPVAL (tup2);
DUMPVAL (tup3); DUMPVAL (tup3);
typedef function<int()> BoundFun; using BoundFun = function<int()>;
BoundFun functor0 = Apply<0>::bind<BoundFun> (fun0, tup0); BoundFun functor0 = bindArgTuple (fun0, tup0);
BoundFun functor1 = Apply<1>::bind<BoundFun> (fun1, tup1); BoundFun functor1 = bindArgTuple (fun1, tup1);
BoundFun functor2 = Apply<2>::bind<BoundFun> (fun2, tup3); BoundFun functor2 = bindArgTuple (fun2, tup2);
BoundFun functor3 = Apply<3>::bind<BoundFun> (fun3, tup3); BoundFun functor3 = bindArgTuple (fun3, tup3);
CHECK (-1 == functor0() );
CHECK (11 == functor1() );
CHECK (11+12 == functor2() );
CHECK (11+12+13 == functor3() );
functor0 = bindArgTuple (fun0, tup0);
functor1 = bindArgTuple (fun1, tup1);
functor2 = bindArgTuple (fun2, tup2);
functor3 = bindArgTuple (fun3, tup3);
CHECK (-1 == functor0() ); CHECK (-1 == functor0() );
CHECK (11 == functor1() ); CHECK (11 == functor1() );
@ -251,84 +180,18 @@ namespace test {
function<int(int,int)> unbound_functor2 (fun2); function<int(int,int)> unbound_functor2 (fun2);
function<int(int,int,int)> unbound_functor3 (fun3); function<int(int,int,int)> unbound_functor3 (fun3);
typedef function<int()> BoundFun; using BoundFun = function<int()>;
BoundFun functor0 = Apply<0>::bind<BoundFun> (unbound_functor0, tup0); BoundFun functor0 = bindArgTuple (unbound_functor0, tup0);
BoundFun functor1 = Apply<1>::bind<BoundFun> (unbound_functor1, tup1); BoundFun functor1 = bindArgTuple (unbound_functor1, tup1);
BoundFun functor2 = Apply<2>::bind<BoundFun> (unbound_functor2, tup3); BoundFun functor2 = bindArgTuple (unbound_functor2, tup2);
BoundFun functor3 = Apply<3>::bind<BoundFun> (unbound_functor3, tup3); BoundFun functor3 = bindArgTuple (unbound_functor3, tup3);
CHECK (-1 == functor0() ); CHECK (-1 == functor0() );
CHECK (11 == functor1() ); CHECK (11 == functor1() );
CHECK (11+12 == functor2() ); CHECK (11+12 == functor2() );
CHECK (11+12+13 == functor3() ); CHECK (11+12+13 == functor3() );
functor0 = bindArgTuple (unbound_functor0, tup0);
functor1 = bindArgTuple (unbound_functor1, tup1);
functor2 = bindArgTuple (unbound_functor2, tup2);
functor3 = bindArgTuple (unbound_functor3, tup3);
CHECK (-1 == functor0() );
CHECK (11 == functor1() );
CHECK (11+12 == functor2() );
CHECK (11+12+13 == functor3() );
}
void
build_closure ()
{
Tuple<TySeq<>> tup0 ;
Tuple<TySeq<int>> tup1 (11);
Tuple<TySeq<int,int>> tup2 (11,12);
Tuple<TySeq<int,int,int>> tup3 (11,12,13);
FunctionClosure<int()> clo0 (fun0,tup0);
FunctionClosure<int(int)> clo1 (fun1,tup1);
FunctionClosure<int(int,int)> clo2 (fun2,tup2);
FunctionClosure<int(int,int,int)> clo3 (fun3,tup3);
CHECK (-1 == clo0() );
CHECK (11 == clo1() );
CHECK (11+12 == clo2() );
CHECK (11+12+13 == clo3() );
function<int()> unbound_functor0 (fun0);
function<int(int)> unbound_functor1 (fun1);
function<int(int,int)> unbound_functor2 (fun2);
function<int(int,int,int)> unbound_functor3 (fun3);
clo0 = FunctionClosure<int()> (unbound_functor0,tup0);
clo1 = FunctionClosure<int(int)> (unbound_functor1,tup1);
clo2 = FunctionClosure<int(int,int)> (unbound_functor2,tup2);
clo3 = FunctionClosure<int(int,int,int)> (unbound_functor3,tup3);
CHECK (-1 == clo0() );
CHECK (11 == clo1() );
CHECK (11+12 == clo2() );
CHECK (11+12+13 == clo3() );
CHECK (-1 == closure(fun0,tup0) () );
CHECK (11 == closure(fun1,tup1) () );
CHECK (11+12 == closure(fun2,tup2) () );
CHECK (11+12+13 == closure(fun3,tup3) () );
CHECK (-1 == closure(unbound_functor0,tup0) () );
CHECK (11 == closure(unbound_functor1,tup1) () );
CHECK (11+12 == closure(unbound_functor2,tup2) () );
CHECK (11+12+13 == closure(unbound_functor3,tup3) () );
// finally combine all techniques....
using NumberzArg = TySeq<List2>::Seq;
using NumberzSig = BuildFunType<int,NumberzArg>::Sig;
Tuple<NumberzArg> numberzTup (Num<5>(22), Num<6>(33), Num<7>(44));
FunctionClosure<NumberzSig> numClo (getNumberz<5,6,7>, numberzTup );
CHECK (22+33+44 == numClo() );
} }
}; };

View file

@ -30,7 +30,7 @@ namespace meta {
namespace test { namespace test {
using ::test::Test; using ::test::Test;
// using lib::test::showType; using lib::test::showType;
using lib::meta::_Fun; using lib::meta::_Fun;
using func::applyFirst; using func::applyFirst;
using func::applyLast; using func::applyLast;
@ -236,7 +236,6 @@ namespace test {
// Version4: as you'd typically do it in real life-------- // // Version4: as you'd typically do it in real life-------- //
/*
fun_23 = func::applyFirst (f, Num<1>(18)); // use the convenience function API to close over a single value fun_23 = func::applyFirst (f, Num<1>(18)); // use the convenience function API to close over a single value
int r5 = fun_23(_2_,_3_).o_; // invoke the resulting functor... int r5 = fun_23(_2_,_3_).o_; // invoke the resulting functor...
@ -276,7 +275,6 @@ namespace test {
CHECK ( 7+6+5 == (func::applyFirst( fun13<7,6,5>, _7_ ) (_6_,_5_)).o_); CHECK ( 7+6+5 == (func::applyFirst( fun13<7,6,5>, _7_ ) (_6_,_5_)).o_);
CHECK ( 6+5 == (func::applyFirst( fun12<6,5>, _6_ ) (_5_)).o_); CHECK ( 6+5 == (func::applyFirst( fun12<6,5>, _6_ ) (_5_)).o_);
CHECK ( 5 == (func::applyFirst( fun11<5>, _5_ ) ( )).o_); CHECK ( 5 == (func::applyFirst( fun11<5>, _5_ ) ( )).o_);
*/
@ -403,9 +401,9 @@ namespace test {
using SigC = _Fun<decltype(chain)>::Sig; using SigC = _Fun<decltype(chain)>::Sig;
using SigP = _Fun<decltype(pappl)>::Sig; using SigP = _Fun<decltype(pappl)>::Sig;
// CHECK (showType<Sig1>() == "double (float&, int&, long)"_expect); CHECK (showType<Sig1>() == "double (float&, int&, long)"_expect);
// CHECK (showType<SigC>() == "long (float&, int&, long)"_expect); CHECK (showType<SigC>() == "long (float&, int&, long)"_expect);
// CHECK (showType<SigP>() == "double (int&, long)"_expect); CHECK (showType<SigP>() == "double (int&, long)"_expect);
CHECK (220 == f1 (ff,ii,33)); CHECK (220 == f1 (ff,ii,33));
CHECK (220 == chain(ff,ii,33)); CHECK (220 == chain(ff,ii,33));

View file

@ -164414,7 +164414,8 @@ Since then others have made contributions, see the log for the history.</font></
<linktarget COLOR="#5d38bc" DESTINATION="ID_667556429" ENDARROW="Default" ENDINCLINATION="23;-31;" ID="Arrow_ID_1911198692" SOURCE="ID_1090818949" STARTARROW="None" STARTINCLINATION="-163;8;"/> <linktarget COLOR="#5d38bc" DESTINATION="ID_667556429" ENDARROW="Default" ENDINCLINATION="23;-31;" ID="Arrow_ID_1911198692" SOURCE="ID_1090818949" STARTARROW="None" STARTINCLINATION="-163;8;"/>
</node> </node>
<node CREATED="1749081571461" ID="ID_1222760176" MODIFIED="1749081586647" TEXT="dann Verwendungen des TupleApplicators eliminieren"/> <node CREATED="1749081571461" ID="ID_1222760176" MODIFIED="1749081586647" TEXT="dann Verwendungen des TupleApplicators eliminieren"/>
<node CREATED="1749082302350" ID="ID_1914498797" MODIFIED="1749082308154" TEXT="BindToArgument umstellen"> <node COLOR="#338800" CREATED="1749082302350" FOLDED="true" ID="ID_1914498797" MODIFIED="1749141642747" TEXT="BindToArgument umstellen">
<icon BUILTIN="button_ok"/>
<node CREATED="1749082310090" ID="ID_618843730" MODIFIED="1749082348031" TEXT="Problem: out-of-bounds Index jetzt anders behandelt"> <node CREATED="1749082310090" ID="ID_618843730" MODIFIED="1749082348031" TEXT="Problem: out-of-bounds Index jetzt anders behandelt">
<node CREATED="1749082386028" ID="ID_1571223641" MODIFIED="1749082388299" TEXT="bisher..."> <node CREATED="1749082386028" ID="ID_1571223641" MODIFIED="1749082388299" TEXT="bisher...">
<node CREATED="1749082348881" ID="ID_1539878278" MODIFIED="1749082385036" TEXT="es wurde Apply&lt;ARG_CNT&gt; ausgew&#xe4;hlt"/> <node CREATED="1749082348881" ID="ID_1539878278" MODIFIED="1749082385036" TEXT="es wurde Apply&lt;ARG_CNT&gt; ausgew&#xe4;hlt"/>
@ -164675,9 +164676,21 @@ Since then others have made contributions, see the log for the history.</font></
<node COLOR="#435e98" CREATED="1749081613141" ID="ID_126616986" MODIFIED="1749081621876" TEXT="FunctionComposition_test"/> <node COLOR="#435e98" CREATED="1749081613141" ID="ID_126616986" MODIFIED="1749081621876" TEXT="FunctionComposition_test"/>
<node COLOR="#435e98" CREATED="1749081619255" ID="ID_945095728" MODIFIED="1749081621876" TEXT="TupleClosure_test"/> <node COLOR="#435e98" CREATED="1749081619255" ID="ID_945095728" MODIFIED="1749081621876" TEXT="TupleClosure_test"/>
</node> </node>
<node CREATED="1749141651868" ID="ID_1929986459" MODIFIED="1749141664903" TEXT="Hilfskonstrukte entfernen">
<node CREATED="1749141665773" ID="ID_1270373632" MODIFIED="1749141670880" TEXT="Apply-Spezialisierungen"/>
<node CREATED="1749141689698" ID="ID_865992669" MODIFIED="1749141693005" TEXT="TupleApplicator"/>
<node CREATED="1749141693742" ID="ID_1131353514" MODIFIED="1749141699732" TEXT="FunctionClosure"/>
<node CREATED="1749141700512" ID="ID_10137882" MODIFIED="1749141707469" TEXT="func::closure()"/>
</node> </node>
</node> </node>
</node> </node>
<node CREATED="1749142101434" ID="ID_1381240660" MODIFIED="1749142108466" TEXT="verbleibende Konstrukte">
<node CREATED="1749142116785" ID="ID_1310351984" MODIFIED="1749142126238" TEXT="func::bindArgTuple"/>
<node CREATED="1749142164570" ID="ID_779039468" MODIFIED="1749142166210" TEXT="func::bindLast"/>
<node CREATED="1749142129186" ID="ID_1530717251" MODIFIED="1749142133129" TEXT="func::chained"/>
<node CREATED="1749142208397" ID="ID_1965271283" MODIFIED="1749142210078" TEXT="func::PApply"/>
</node>
</node>
</node> </node>
</node> </node>
<node COLOR="#435e98" CREATED="1748829133428" ID="ID_266502224" MODIFIED="1748884086384" TEXT="Tuple"> <node COLOR="#435e98" CREATED="1748829133428" ID="ID_266502224" MODIFIED="1748884086384" TEXT="Tuple">