¿Qué funciones funcionales merecen un poco de confusión OOP por los beneficios que brindan?

13

Después de aprender la programación funcional en Haskell y F #, el paradigma de la programación orientada a objetos (OOP, por sus siglas en inglés) parece estar muy atrás con clases, interfaces, objetos. ¿Qué aspectos de la PF puedo llevar al trabajo que mis compañeros de trabajo puedan entender? ¿Merece la pena hablar con mi jefe sobre algún estilo de FP para volver a capacitar a mi equipo para que podamos usarlo?

Aspectos posibles de FP:

  • Inmutabilidad
  • Solicitud parcial y curry
  • Funciones de primera clase (punteros a función / objetos funcionales / patrón de estrategia)
  • Evaluación perezosa (y mónadas)
  • Funciones puras (sin efectos secundarios)
  • Expresiones (frente a declaraciones: cada línea de código produce un valor en lugar de, o además de causar efectos secundarios)
  • Recursión
  • Coincidencia de patrones

¿Es un programa gratuito para todos donde podemos hacer lo que el lenguaje de programación admite hasta el límite que lo admite? ¿O hay una mejor pauta?

    
pregunta bonomo 13.11.2013 - 04:55

6 respuestas

12

La programación funcional es un paradigma diferente de la programación orientada a objetos (una mentalidad diferente y una manera diferente de pensar acerca de los programas). Ha comenzado a darse cuenta de que aquí hay más de una manera (orientada a objetos) para pensar acerca de los problemas y sus soluciones. Hay otros (la programación procedural y genérica vienen a la mente). La forma en que reaccione a este nuevo conocimiento, ya sea que acepte e integre estas nuevas herramientas y enfoques en su conjunto de habilidades, determinará si usted crece y se convierte en un desarrollador más completo y calificado.

Todos estamos capacitados para manejar y nos sentimos cómodos con un cierto nivel de complejidad. Me gusta llamar a esto el límite hrair de una persona (de Watership Down, ¿qué tan alto puede contar)? Es una gran cosa para expandir su mente, su capacidad para considerar más opciones y tener más herramientas para abordar y resolver problemas. Pero es un cambio, y te saca de tu zona de confort.

Un problema que puede encontrar es que se sentirá menos contento de seguir a la multitud de "todo es un objeto". Es posible que tenga que desarrollar paciencia mientras trabaja con personas que tal vez no entiendan (o quieran entender) por qué un enfoque funcional para el desarrollo de software funciona bien para ciertos problemas. Así como un enfoque de programación genérico funciona bien para ciertos problemas.

¡Buena suerte!

    
respondido por el ChuckCottrill 13.11.2013 - 05:17
6

La programación funcional produce resultados muy prácticos, prácticos La productividad en la escritura de código de todos los días: algunas características favorecen terseness, que es genial porque cuanto menos código escribas, menos fallas que usted hace, y se requiere menos mantenimiento.

Siendo matemático, me parece muy atractivo lo funcional y sofisticado, pero suele ser útil a la hora de diseñar una aplicación: estas estructuras pueden codificar en la estructura del programa una gran cantidad de invariantes de El programa, sin representar estas invariantes por variables.

Mi combinación favorita puede parecer bastante trivial, sin embargo creo Que tiene un impacto de productividad muy alto. Esta combinación es parcial Application and Currying y First Class Functions que yo haría volver a etiquetar nunca volver a escribir un for-loop : en lugar de pasar el cuerpo del bucle a una función de iteración o mapeo. Me contrataron recientemente para un C ++ trabajo y me di cuenta divertidamente, perdí totalmente el hábito de escribir for-loops!

La combinación de Recursion y Pattern Matching aniquila la necesidad de ese patrón de diseño de visitante . Solo compara el código que necesitas Programar un evaluador de expresiones booleanas: en cualquier funcional. lenguaje de programación, esto debería ser alrededor de 15 líneas de código, en OOP lo correcto hacer es usar el patrón de diseño de Visitante , que convierte ese Ejemplo de juguete en un extenso ensayo. Las ventajas son obvias y no lo soy. consciente de cualquier inconveniente.

    
respondido por el user40989 13.11.2013 - 18:16
5

Es posible que tengas que restringir las partes de tu conocimiento que usas en el trabajo, la forma en que Superman tiene que fingir ser Clark Kent para disfrutar de los beneficios de una vida normal. Pero saber más nunca te hará daño. Dicho esto, algunos aspectos de la Programación funcional son apropiados para una tienda orientada a objetos, y otros aspectos pueden valer la pena hablar con su jefe para que pueda aumentar el nivel de conocimiento promedio de su tienda y poder escribir un mejor código como resultado.

FP y OOP no son mutuamente excluyentes. Mira a Scala. Algunos piensan que es lo peor porque es FP impuro, pero otros piensan que es lo mejor por la misma razón.

Uno por uno, aquí hay algunos aspectos que funcionan muy bien con OOP:

  • Funciones puras (sin efectos secundarios): todos los lenguajes de programación que conozco lo admiten. Hacen que su código sea mucho más fácil de razonar y se debe usar siempre que sea práctico. No tienes que llamarlo FP. Simplemente llámelo buenas prácticas de codificación.

  • Inmutabilidad: la cadena es posiblemente el objeto Java más utilizado y es inmutable. Cubro Objetos de Java inmutables y Colecciones de Java inmutables en mi blog. Algo de eso puede ser aplicable a usted.

  • Funciones de primera clase (punteros de función / Objetos funcionales / Patrón de estrategia) - Java ha tenido una versión mutante de esto desde la versión 1.1 con la mayoría de las clases de API (y hay cientos) que implementan la interfaz de Oyente . Runnable es probablemente el objeto funcional más utilizado. Las funciones de primera clase son más trabajo para codificar en un lenguaje que no las admite de forma nativa, pero a veces vale la pena el esfuerzo adicional cuando simplifican otros aspectos de su código.

  • La recursión es útil para procesar árboles. En una tienda OOP, ese es probablemente el uso primario apropiado de la recursión. El uso de la recursión para divertirse en OOP probablemente debería estar mal visto si, por ninguna otra razón, la mayoría de los idiomas OOP no tienen el espacio de pila por defecto para que sea una buena idea.

  • Expresiones (frente a declaraciones: cada línea de código produce un valor en lugar de, o además de causar efectos secundarios): el único operador evaluativo en C, C ++ y Java es Operador Ternario . Discuto el uso apropiado en mi blog. Puede encontrar que escribe algunas funciones simples que son altamente reutilizables y evaluativas.

  • Evaluación perezosa (y mónadas): principalmente restringida a Inicialización perezosa en POO. Sin las características de idioma para admitirlo, es posible que encuentre algunas API que sean útiles, pero escribirlas es difícil. Maximice su uso de flujos en su lugar: vea las interfaces de Writer y Reader para ver ejemplos.

  • Aplicación parcial y curry: no es práctico sin funciones de primera clase.

  • Coincidencia de patrones: generalmente no se recomienda en OOP.

En resumen, no creo que el trabajo deba ser libre para todos, en el que pueda hacer lo que sea que el lenguaje de programación admita hasta el límite que lo admite. Creo que la legibilidad por parte de sus compañeros de trabajo debería ser su prueba de fuego para el código hecho para ser contratado. Cuando eso te moleste más, me gustaría iniciar una educación en el trabajo para ampliar los horizontes de tus compañeros de trabajo.

    
respondido por el GlenPeterson 13.11.2013 - 17:05
3

Además de la programación funcional y la programación orientada a objetos, también hay una programación declarativa (SQL, XQuery). Aprender cada estilo lo ayuda a obtener nuevos conocimientos y aprenderá a elegir la herramienta adecuada para el trabajo.

Pero sí, puede ser muy frustrante escribir código en un idioma, y saber que si estuviera usando otra cosa, podría ser mucho más productivo para un dominio de problemas en particular. Sin embargo, incluso si está utilizando un lenguaje como Java, es posible aplicar conceptos de FP a su código Java, aunque de manera indirecta. El marco de guayaba, por ejemplo, hace algo de esto.

    
respondido por el sgwizdak 13.11.2013 - 07:20
2

Como programador, creo que nunca debes dejar de aprender. Dicho esto, es muy interesante que aprender FP esté afectando tus habilidades OOP. Tiendo a pensar en aprender OOP como aprender a andar en bicicleta; nunca olvidas cómo hacerlo.

A medida que aprendí los entresijos de FP, me encontré pensando más matemáticamente y obtuve una mejor perspectiva de los medios en los que escribo software. Esa es mi experiencia personal.

A medida que adquiera más experiencia, los conceptos básicos de programación serán mucho más difíciles de perder. Así que te sugiero que te tomes las cosas con calma en la PF hasta que los conceptos de POO estén totalmente consolidados en tu mente. FP es un cambio de paradigma definido. Buena suerte!

    
respondido por el Bobby Gammill 13.11.2013 - 05:03
0

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;
}
    
respondido por el alrikai 08.02.2014 - 02:46