template<typename T>
struct is_vararg
: std::false_type
{};
template<typename T>
struct is_vararg<vararg<T>>
: std::true_type
{};
template<typename A, typename T, typename F, typename Ts>
struct subst1_
: id<list<list<T>>>
{};
template<typename T, typename F, typename Ts>
struct subst1_<F, T, F, Ts>
: id<list<>>
{};
template<typename A, typename T, typename F, typename Ts>
struct subst1_<vararg<A>, T, F, Ts>
: id<list<eval<Ts>>>
{};
template<typename As, typename Ts>
using substitutions_ =
push_back<join<transform<
concat<As, repeat_n_c<size<Ts>{} + 2 - size<As>{}, back<As>>>,
concat<Ts, list<back<As>, back<As>>>,
compose<quote<eval>,
bind_back<
quote<subst1_>,
back<As>,
lazy::drop<Ts, minus<size<As>, meta::size_t<2>>>>>>>, list<back<As>>>;
template<typename As>
using is_variadic = is_vararg<at<push_front<As, void>, dec<size<As>>>>;
template<typename As, typename Ts>
using substitutions =
eval<if_c<((is_variadic<As>::value && size<Ts>::value + 2 >= size<As>::value) ||
(size<Ts>::value + 1 == size<As>::value)), defer<substitutions_, As, Ts>>>;
template<int, typename...As>
struct lambda2_
{
private:
template<int N, typename...Us> friend struct lambda2_;
using Tags = list<As...>; // Includes the lambda body as the last arg!
template<typename T, typename Args> struct impl;
template<typename Args>
using eval_impl = compose<quote<eval>, bind_back<quote<impl>, Args>>;
template<template<typename...> class C, typename Args, typename Ts>
using subst_ = apply_list<quote<C>, join<transform<Ts, eval_impl<Args>>>>;
template<typename, typename, typename = void>
struct impl_
{};
template<template<typename...> class C, typename...Ts, typename Args>
struct impl_<defer<C, Ts...>, Args, void_<subst_<C, Args, list<Ts...>>>>
{
using type = list<subst_<C, Args, list<Ts...>>>;
};
template<typename T, typename Args>
struct impl
: if_<in<Tags, T>, lazy::at<Args, reverse_find_index<Tags, T>>, id<list<T>>>
{};
template<template<typename...> class C, typename...Ts, typename Args>
struct impl<defer<C, Ts...>, Args> : impl_<defer<C, Ts...>, Args>
{};
template<template<typename...> class C, typename...Ts, typename Args>
struct impl<C<Ts...>, Args> : impl_<defer<C, Ts...>, Args>
{};
template<int N, typename...Bs, typename Args>
struct impl<lambda2_<N, Bs...>, Args>
{
using type = list<compose<
typename lambda2_<0, As..., Bs...>::thunk,
bind_front<quote<concat>, Args>,
curry<bind_front<quote<substitutions>, list<Bs...>>>>>;
};
struct thunk
{
template<typename S, typename R = eval<impl<back<Tags>, S>>>
using apply = if_c<size<R>{} == 1, front<R>>;
};
public:
template<typename...Ts>
using apply = meta::apply<thunk, substitutions<Tags, list<Ts...>>>;
};
template<typename...As>
using lambda2 = lambda2_<0, As...>;