Ya hay muchas respuestas buenas, por lo que la mía abordará un subconjunto de su pregunta; Es decir, no me gusta la premisa de su pregunta, ya que la POO y las características funcionales no se excluyen mutuamente.
Si usa C ++ 11, hay muchas de estas funciones de programación funcional integradas en la biblioteca de idioma / estándar que se sinergizan (bastante) bien con OOP. Por supuesto, no estoy seguro de qué tan bien recibirán TMP su jefe o compañeros de trabajo, pero el punto es que puede obtener muchas de estas características de una forma u otra en lenguajes no funcionales / OOP, como C ++.
El uso de plantillas con tiempo de compilación se basa en tus primeros 3 puntos,
- Inmutabilidad
- Recursión
- Coincidencia de patrones
En que los valores de la plantilla son inmutables (constantes en tiempo de compilación), cualquier iteración se realiza mediante la recursión, y la bifurcación se realiza mediante (más o menos) coincidencia de patrones, en forma de resolución de sobrecarga.
En cuanto a los otros puntos, el uso de std::bind
y std::function
le da una aplicación de función parcial, y los punteros de función están integrados en el idioma. Los objetos recuperables son objetos funcionales (así como una aplicación de función parcial). Tenga en cuenta que, por objetos invocables, me refiero a los que definen su operator ()
.
La evaluación perezosa y las funciones puras serían un poco más difíciles; para funciones puras, puede usar funciones lambda que solo capturan por valor, pero esto no es ideal.
Por último, aquí hay un ejemplo del uso de la recursión en tiempo de compilación con la aplicación de función parcial. Es un ejemplo un tanto artificial, pero demuestra la mayoría de los puntos anteriores. Unirá recursivamente los valores de una tupla dada a una función dada y generará un objeto de función (llamable)
#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;
}