There are many good answers already, so mine will address a subset of your question; namely, I take umbrage to the premise of your question, as OOP and functional features aren't mutually exclusive.
If you use C++11, there are a lot of these sorts of functional-programming features built into the language/standard library that synergize (pretty) well with OOP. Of course, I'm not sure how well TMP will be received by your boss or coworkers, but the point is you can get many of these features in some form or another in non-functional/OOP languages, like C++.
Using templates with compile time recursion relies on your first 3 points,
- Immutability
- Recursion
- Pattern Matching
In that template-values are immutable (compile-time constants), any iteration is done using recursion, and branching is done using (more or less) pattern matching, in the form of overload resolution.
As for the other points, using std::bind
and std::function
gives you partial function application, and function pointers are built-in to the language. Callable objects are functional objects (as well as partial function application). Note that by callable objects, I mean ones that define their operator ()
.
Lazy evaluation and pure functions would be a bit harder; for pure functions, you can use lambda functions which only capture by value, but this is non-ideal.
Lastly, here's an example of using compile-time recursion with partial function application. It's a somewhat contrived example, but it demonstrates most of the points above. It'll recursively bind the values in a given tuple to a given function and generate a (callable) function object
#include <iostream>
#include <functional>
//holds a compile-time index sequence
template<std::size_t ... >
struct index_seq
{};
//builds the index_seq<...> struct with the indices (boils down to compile-time indexing)
template<std::size_t N, std::size_t ... Seq>
struct gen_indices
: gen_indices<N-1, N-1, Seq ... >
{};
template<std::size_t ... Seq>
struct gen_indices<0, Seq ... >
{
typedef index_seq<Seq ... > type;
};
template <typename RType>
struct bind_to_fcn
{
template <class Fcn, class ... Args>
std::function<RType()> fcn_bind(Fcn fcn, std::tuple<Args...> params)
{
return bindFunc(typename gen_indices<sizeof...(Args)>::type(), fcn, params);
}
template<std::size_t ... Seq, class Fcn, class ... Args>
std::function<RType()> bindFunc(index_seq<Seq...>, Fcn fcn, std::tuple<Args...> params)
{
return std::bind(fcn, std::get<Seq>(params) ...);
}
};
//some arbitrary testing function to use
double foo(int x, float y, double z)
{
return x + y + z;
}
int main(void)
{
//some tuple of parameters to use in the function call
std::tuple<int, float, double> t = std::make_tuple(1, 2.04, 0.1);
typedef double(*SumFcn)(int,float,double);
bind_to_fcn<double> binder;
auto other_fcn_obj = binder.fcn_bind<SumFcn>(foo, t);
std::cout << other_fcn_obj() << std::endl;
}