Il existe déjà de nombreuses bonnes réponses, donc la mienne répondra à un sous-ensemble de votre question; à savoir, je prends ombrage à la prémisse de votre question, car la POO et les fonctionnalités ne s'excluent pas mutuellement.
Si vous utilisez C ++ 11, il y a beaucoup de ces types de fonctionnalités de programmation fonctionnelle intégrées dans la bibliothèque langage / standard qui se synergisent (assez) bien avec la POO. Bien sûr, je ne sais pas dans quelle mesure TMP sera bien reçu par votre patron ou vos collègues, mais le fait est que vous pouvez obtenir bon nombre de ces fonctionnalités sous une forme ou une autre dans des langages non fonctionnels / OOP, comme C ++.
L'utilisation de modèles avec la récursion de temps de compilation repose sur vos 3 premiers points,
- Immutabilité
- Récursivité
- Correspondance de motifs
Dans la mesure où les valeurs de modèle sont immuables (constantes au moment de la compilation), toute itération est effectuée à l'aide de la récursivité, et la ramification est effectuée à l'aide (plus ou moins) de correspondance de modèle, sous la forme d'une résolution de surcharge.
Comme pour les autres points, l'utilisation de std::bind
et std::function
vous donne une application de fonction partielle et des pointeurs de fonction sont intégrés au langage. Les objets appelables sont des objets fonctionnels (ainsi qu'une application de fonction partielle). Notez que par objets appelables, je veux dire ceux qui définissent leur operator ()
.
L'évaluation paresseuse et les fonctions pures seraient un peu plus difficiles; pour les fonctions pures, vous pouvez utiliser des fonctions lambda qui ne capturent que par valeur, mais ce n'est pas idéal.
Enfin, voici un exemple d'utilisation de la récursion au moment de la compilation avec une application de fonction partielle. C'est un exemple quelque peu artificiel, mais il démontre la plupart des points ci-dessus. Il liera récursivement les valeurs d'un tuple donné à une fonction donnée et générera un objet fonction (appelable)
#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;
}