Higher-order functions with templates?
I have been programming in Clojure (functional language) for a while now and I have to use C ++ for the class. I am trying to use some of the features I enjoyed in Clojure (like higher order functions, lambdas, threading arguments, dynamic typing, etc.), but I ran into some brick walls.
First, I implemented a function get
that takes two arguments:
- a collection (vector, list, hashmap, etc.) and
- index
and returns the element at that index.
I also implemented a function conj
that takes two arguments:
- a collection (vector, list, queue, etc.) and
- item / object (any type of collection)
and returns the collection with the added item. In the case of vectors, this is pretty much the same as push_back
.
Now I want to be able to "forward" or "thread" arguments using higher order functions:
using std::vector;
vector<double> my_vec;
forward(my_vec, // take "my_vec"
conj(0.1), // "push" the value of 0.1 to the back of "my_vec"
get(0), // retrieve the first value
inc); // increment that value
This is the same as inc(get(conj(my_vec, 0.1), 0);
but a lot (!) More readable.
The return value forward
in this case should be 1.1.
To make the function forward
work, the arguments after the initial argument must be higher-order functions. That is, they should work similarly to the following:
template<typename Func>
Func get(int i){
return [i](vector<boost::any> coll)
-> boost::optional<boost::any> {
return get(coll, i);
};
}
However, the compiler cannot infer the return type of the lambda function. Also, my guess, based on my extremely limited experience with boost::any
, is that it won't be able to convert vector<double>
to vector<boost::any>
, despite seeming claims boost::any
that it can act as a replacement for just about any type.
I want the function to get
be generic, so I don't want to use boost::function<double (vector <double>, int)>
or any similar concrete typing.
Also, I use boost::optional
instead vector
to return null_ptr
if the index requested from get
is out of scope.
It's okay what my function looks like forward
:
template <typename T1>
optional<T1> forward (T1 expr1){
return expr1;
}
template <typename T1, typename T2>
optional<T1> forward (T1 expr1, T2 expr2){
return forward(expr2(expr1));
}
template <typename T1, typename T2, typename T3>
optional<T1> forward (T1 expr1, T2 expr2, T3 expr3){
return forward(expr2(expr1), expr3);
}
etc.....
Any ideas on how to get this feature forward
to work?
I am also sure there is a more efficient way to implement it than doing arity overloading like I did.
source to share
Just an addition to Horstling's answers, it is actually forward
much simpler to implement:
template <typename Value>
Value forward(Value v) {
return v;
}
template <typename Value, typename Func, typename... Funcs>
auto forward(Value v, Func f, Funcs... fs) -> decltype(forward(f(v), fs...)) {
return forward(f(v), fs...);
}
And C ++ 14 sweetens things up a bit:
template <typename Value>
Value forward(Value v) {
return v;
}
template <typename Value, typename Func, typename... Funcs>
decltype(auto) forward(Value v, Func f, Funcs... fs) {
return forward(f(v), fs...);
}
Take a look at the complete code here (Coliru seems to support boost)
source to share
What can I think of:
http://coliru.stacked-crooked.com/a/039905c5deff8dcf
Instead of lambdas, I used full-fledged functors in three different ways. It supports your example and doesn't require type erasure or such (like boost :: any or std :: function).
boost::optional<double> result = forward(my_vec, conj(0.1), get(0), inc);
In addition, the function is forward
implemented as a variational template allowing any number of functions.
The code is not politicized, but maybe it can provide some inspiration.
EDIT: Anton is absolutely right, my forward implementation was unnecessarily complicated. Now the link above points to its revised version of the code.
source to share