-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathfunctional.h
192 lines (171 loc) · 6.11 KB
/
functional.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
#pragma once
#include <functional>
#include <tuple>
#include <utility>
#include <type_traits>
namespace kaiu {
namespace detail {
/*
* InvokeWithTuple<Result, Functor, CurriedArgs...>(func, tuple<CurriedArgs...>)
*
* Calls a function, passing parameters from tuple
*/
template <typename Result, typename Functor, typename Args>
Result invoke_with_tuple(Functor func, Args tuple);
template <typename Result, typename Functor, typename Args, size_t ArgCount,
size_t... Indices>
Result invoke_shuffle_args(Functor func, std::index_sequence<Indices...> indices, Args tuple);
/*
* CurriedFunction<Result, Arity, Functor, CurriedArgs...>(func, [args...])
*
* Wraps a function. Enables currying and partial application. Currying is
* done by value via the "<<" operator, and partial application is done by
* reference (when possible) via the "apply" method. The functor can be invoked
* via the "invoke" method or the "()" operator.
*/
template <typename Result, size_t Arity, typename Functor, typename... CurriedArgs>
struct CurriedFunction {
private:
using ArgsTuple = std::tuple<CurriedArgs...>;
public:
static const auto is_curried_function = true;
using result_type = Result;
using functor_type = Functor;
static constexpr auto arity = Arity - sizeof...(CurriedArgs);
CurriedFunction(Functor func);
CurriedFunction(Functor func, const ArgsTuple& curried_args);
/*
* Function operator ALWAYS invokes, to partially apply use the "apply"
* method or the curry "<<" operator. A previous implementation permitted
* partial application using the function operator until sufficient
* arguments were available to invoke, but that could lead to difficult
* debugging scenarios involving 100+line template errors, so I separated
* "apply" and "invoke" into two different syntaxes.
*
* operator ()(args): same as .apply(args).invoke()
*/
template <typename... ExtraArgs>
typename std::enable_if<(sizeof...(ExtraArgs) > 0), Result>::type
operator () (ExtraArgs&&... extra_args) const;
/* With no parameters, same as .invoke() */
template <typename... ExtraArgs>
typename std::enable_if<(sizeof...(ExtraArgs) == 0), Result>::type
operator () (ExtraArgs&&... extra_args) const;
/*
* Left-shift operator to curry
*
* Similar as .apply(arg), but captures by VALUE instead of by reference,
* unless std::reference_wrapper is used.
*/
template <typename Arg>
CurriedFunction<Result, Arity, Functor, CurriedArgs..., Arg&>
operator << (std::reference_wrapper<Arg> ref) const;
template <typename Arg>
CurriedFunction<Result, Arity, Functor, CurriedArgs..., typename std::decay<Arg>::type>
operator << (Arg&& arg) const;
/*
* Partial application, returns a new functor, with the extra arguments
* bound. It captures by REFERENCE where possible. To capture by value,
* either use static_cast, remove_reference, decay, or operator <<.
*/
template <typename... ExtraArgs>
CurriedFunction<Result, Arity, Functor, CurriedArgs..., ExtraArgs...>
apply (ExtraArgs&&... extra_args) const;
/* Calls the function */
template <size_t Arity_ = Arity>
typename std::enable_if<(sizeof...(CurriedArgs) == Arity_), Result>::type
invoke() const;
/* Always returns false */
bool operator == (nullptr_t) const;
/* Always returns true */
bool operator != (nullptr_t) const;
private:
template <size_t NumArgs>
void statically_check_args_count_for_invoke() const;
Functor func;
ArgsTuple curried_args;
};
}
template <typename T>
struct is_curried_function {
private:
template <typename U>
static std::integral_constant<bool, U::is_curried_function> check(int);
template <typename>
static std::false_type check(...);
public:
static constexpr auto value = decltype(check<T>(0))::value;
};
template <typename Result, size_t Arity, typename Functor, typename... CurriedArgs>
using Curried = detail::CurriedFunction<Result, Arity, Functor, CurriedArgs...>;
/* Curry-wrap std::function */
template <typename Result, typename... Args>
const auto curry_wrap(std::function<Result(Args...)> functor)
{
return Curried<Result, sizeof...(Args), decltype(functor)>(functor);
}
/* Curry-wrap plain function */
template <typename Result, typename... Args>
const auto curry_wrap(Result (&functor)(Args...))
{
return Curried<Result, sizeof...(Args), decltype(functor)>(functor);
}
/* Curry-wrap functor */
template <typename Result, size_t Arity, typename Functor>
const auto curry_wrap(Functor functor)
{
return Curried<Result, Arity, Functor>(functor);
}
/* Call a function using arguments stored in a tuple */
template <typename Result, typename Functor, typename Args>
Result invoke(Functor func, Args args);
namespace functional_chain {
/***
* Bind operator
*
* This is a horrible idea, added only for fun. Please DO NOT use it in
* production! Or change the operator to any left-to-right operator that isn't
* the comma!
*
* Adds operator such that;
* "T t , U(T) u" ⇒ "u(t)"
*
* Giving us this syntax for chain operations:
* "T t , U(T) u , V(U) v" ⇒ "v(u(t))"
*
* Given the requirements for left side <t> and right-side <u>:
* - <u> is a curry-wrapped function
* - <u> takes one parameter (after any currying)
*
* You MUST compile with '-Wunused-value', in order to detect when the operator
* has not been chosen by the compiler (since a no-op default will silently be
* used in its place, triggering an unused-value warning)
*/
template <typename From, typename To,
typename DFrom = typename std::decay<From>::type,
typename DTo = typename std::decay<To>::type>
typename std::enable_if<
is_curried_function<DTo>::value &&
DTo::arity == 1,
typename DTo::result_type>::type
operator ,(From&& from, To to)
{
return to(std::forward<From>(from));
}
/* Disable comma operator if we think you've made a mistake */
template <typename From, typename To,
typename DFrom = typename std::decay<From>::type,
typename DTo = typename std::decay<To>::type>
typename std::enable_if<
is_curried_function<DTo>::value &&
DTo::arity != 1,
typename DTo::result_type>::type
operator ,(From&& from, To to)
{
static_assert(DTo::arity == 1, "Functional bind cannot be implemented: Functor has arity != 1");
}
}
}
#ifndef functional_tcc
#include "functional.tcc"
#endif