ventajas programacion paradigmas orientada objetos libros funcional ejemplos desventajas functional-programming clojure lisp model-driven-development

functional programming - programacion - ¿Existe una metodología de ingeniería de software para la programación funcional?



programacion funcional ventajas y desventajas (13)

Bien,

En general, muchos lenguajes de programación funcional se utilizan en las universidades durante mucho tiempo para "problemas de pequeños juguetes".

Se están volviendo más populares ahora, ya que OOP tiene dificultades con la "programación paralela" debido al "estado". Y, en ocasiones, el estilo funcional es mejor para el problema en cuestión, como Google MapReduce.

Estoy seguro de que, cuando los individuos de la función chocan contra la pared [intenta implementar sistemas de más de 1.000.000 de líneas de código], algunos de ellos vendrán con nuevas metodologías de ingeniería de software con palabras de moda :-). Deben responder a la vieja pregunta: ¿Cómo dividir el sistema en partes para que podamos "morder" cada una de una en una? [trabajo iterativo, inceremental y de manera evolutiva] usando el estilo funcional.

Es seguro que el estilo funcional afectará nuestro estilo orientado a objetos. "Todavía" utilizamos muchos conceptos de sistemas funcionales y adaptados a nuestros idiomas OOP.

Pero, ¿se utilizarán programas funcionales para sistemas tan grandes? ¿Se convertirán en la corriente principal? Esa es la pregunta .

Y nadie puede venir con una metodología realista sin implementar un sistema tan grande, ensuciándose las manos. Primero debes ensuciarte las manos y luego sugerir una solución. Soluciones-Sugerencias sin "dolores reales y suciedad" serán "fantasía".

La ingeniería de software, tal como se enseña hoy, está totalmente centrada en la programación orientada a objetos y la visión "natural" orientada a objetos del mundo. Existe una metodología detallada que describe cómo transformar un modelo de dominio en un modelo de clase con varios pasos y una gran cantidad de artefactos (UML) como diagramas de casos de uso o diagramas de clases. Muchos programadores han internalizado este enfoque y tienen una buena idea sobre cómo diseñar una aplicación orientada a objetos desde cero.

El nuevo bombo es la programación funcional, que se enseña en muchos libros y tutoriales. Pero ¿qué pasa con la ingeniería de software funcional? Mientras leía acerca de Lisp y Clojure, encontré dos declaraciones interesantes:

  1. Los programas funcionales a menudo se desarrollan de abajo hacia arriba en lugar de de arriba abajo (''On Lisp'', Paul Graham)

  2. Los programadores funcionales usan mapas donde los programadores de OO usan objetos / clases (''Clojure for Java Programmers'', charla de Rich Hickley).

Entonces, ¿cuál es la metodología para un diseño sistemático (basado en un modelo?) De una aplicación funcional, es decir, en Lisp o Clojure? ¿Cuáles son los pasos comunes, qué artefactos utilizo, cómo los asigno del espacio problemático al espacio de la solución?


Descubrí que Behavior Driven Development es un ajuste natural para el código de rápido desarrollo tanto en Clojure como en SBCL. La ventaja real de aprovechar BDD con un lenguaje funcional es que tiendo a escribir pruebas unitarias de grano más fino de lo que normalmente uso al usar lenguajes de procedimiento porque hago un trabajo mucho mejor de descomponer el problema en partes más pequeñas de funcionalidad.


El diseño orientado a objetos no es lo mismo que la ingeniería de software. La ingeniería de software tiene que ver con todo el proceso de cómo pasamos de los requisitos a un sistema operativo, a tiempo y con una tasa de defectos baja. La programación funcional puede ser diferente de la OO, pero no elimina los requisitos, los diseños de alto nivel y detallados, la verificación y las pruebas, las métricas de software, la estimación y todo lo demás "cosas de ingeniería de software".

Además, los programas funcionales exhiben modularidad y otra estructura. Sus diseños detallados deben expresarse en términos de los conceptos en esa estructura.


Existe el estilo de "cálculo de programa" / "diseño por cálculo" asociado con el Prof. Richard Bird y el grupo de Álgebra de Programación en la Universidad de Oxford (Reino Unido), no creo que sea demasiado exagerado considerar esto como una metodología.

Personalmente, aunque me gusta el trabajo producido por el grupo AoP, no tengo la disciplina para practicar el diseño de esta manera. Sin embargo, ese es mi defecto, y no uno de cálculo del programa.


Gracias a Dios que la gente de ingeniería de software aún no ha descubierto la programación funcional. Aquí hay algunos paralelos:

  • Muchos "patrones de diseño" de OO se capturan como funciones de orden superior. Por ejemplo, el patrón Visitante se conoce en el mundo funcional como un "pliegue" (o si usted es un teórico de cabeza puntiaguda, un "catamorfismo"). En los lenguajes funcionales, los tipos de datos son en su mayoría árboles o tuplas, y cada tipo de árbol tiene un catamorfismo natural asociado.

    Estas funciones de orden superior a menudo vienen con ciertas leyes de programación, también conocidas como "teoremas libres".

  • Los programadores funcionales utilizan diagramas mucho menos que los programadores OO. Gran parte de lo que se expresa en los diagramas OO se expresa, en cambio, en tipos o en "firmas", que debería considerarse como "tipos de módulos". Haskell también tiene "clases de tipo", que es un poco como un tipo de interfaz.

    Aquellos programadores funcionales que usan tipos generalmente piensan que "una vez que se tienen los tipos correctos, el código prácticamente se escribe a sí mismo".

    No todos los lenguajes funcionales utilizan tipos explícitos, pero el libro Cómo diseñar programas , un excelente libro para aprender Scheme / Lisp / Clojure, se basa en gran medida en las "descripciones de datos", que están estrechamente relacionadas con los tipos.

Entonces, ¿cuál es la metodología para un diseño sistemático (basado en un modelo?) De una aplicación funcional, es decir, en Lisp o Clojure?

Cualquier método de diseño basado en la abstracción de datos funciona bien. Resulta que creo que esto es más fácil cuando el lenguaje tiene tipos explícitos, pero funciona incluso sin él. Un buen libro sobre métodos de diseño para tipos de datos abstractos, que se adapta fácilmente a la programación funcional, es Abstraction and Specification in Program Development de Barbara Liskov y John Guttag, la primera edición. Liskov ganó el premio Turing en parte por ese trabajo.

Otra metodología de diseño que es exclusiva de Lisp es decidir qué extensiones de idioma serían útiles en el dominio del problema en el que está trabajando y luego usar macros higiénicas para agregar estas construcciones a su idioma. Un buen lugar para leer sobre este tipo de diseño es el artículo de Matthew Flatt, Creación de idiomas en la raqueta . El artículo puede estar detrás de un muro de pago. También puede encontrar más material general sobre este tipo de diseño buscando el término "lenguaje incorporado específico del dominio"; Para consejos y ejemplos particulares más allá de lo que cubre Matthew Flatt, probablemente comenzaría con Graham''s On Lisp o quizás con ANSI Common Lisp .

¿Cuáles son los pasos comunes, qué artefactos utilizo?

Pasos comunes:

  1. Identifique los datos en su programa y las operaciones en él, y defina un tipo de datos abstracto que represente estos datos.

  2. Identifique acciones comunes o patrones de cómputo y expréselas como macros o funciones de orden superior. Espera dar este paso como parte de la refactorización.

  3. Si está utilizando un lenguaje funcional escrito, use el verificador de tipos temprano y con frecuencia. Si está utilizando Lisp o Clojure, la mejor práctica es escribir primero los contratos de funciones, incluidas las pruebas unitarias; es un desarrollo impulsado por pruebas al máximo. Y querrá usar cualquier versión de QuickCheck que se haya portado a su plataforma, que en su caso parece que se llama ClojureCheck . Es una biblioteca extremadamente poderosa para construir pruebas aleatorias de código que utiliza funciones de orden superior.


La programación OO acopla estrechamente los datos con el comportamiento. La programación funcional separa los dos. Por lo tanto, no tiene diagramas de clase, pero sí tiene estructuras de datos y, en particular, tiene tipos de datos algebraicos. Esos tipos se pueden escribir para que coincidan muy estrechamente con su dominio, incluida la eliminación de valores imposibles por construcción.

Así que no hay libros y libros sobre él, pero hay un enfoque bien establecido para, como dice el refrán, hacer que los valores imposibles de representar.

Al hacerlo, puede hacer un rango de opciones sobre la representación de ciertos tipos de datos como funciones y, a la inversa, representar ciertas funciones como una unión de tipos de datos para que pueda obtener, por ejemplo, serialización, especificaciones más estrictas, optimización, etc. .

Luego, dado eso, escribe funciones sobre sus anuncios de manera que establezca algún tipo de álgebra , es decir, hay leyes fijas que se aplican a estas funciones. Algunos son quizás idempotentes, lo mismo después de múltiples aplicaciones. Algunos son asociativos. Algunos son transitivos, etc.

Ahora tiene un dominio sobre el que tiene funciones que se componen de acuerdo con leyes de buen comportamiento. Un simple DSL incrustado!

Ah, y dadas las propiedades, puede, por supuesto, escribir pruebas aleatorias automatizadas de ellos (ala QuickCheck) ... y eso es solo el comienzo.


Para Clojure, recomiendo volver al buen modelo relacional antiguo. Fuera del Tarpit es una lectura inspiradora.


Personalmente, encuentro que todas las buenas prácticas habituales del desarrollo de OO se aplican también a la programación funcional, solo con unos pequeños ajustes para tener en cuenta la cosmovisión funcional. Desde una perspectiva metodológica, realmente no es necesario hacer nada fundamentalmente diferente.

Mi experiencia proviene de haber pasado de Java a Clojure en los últimos años.

Algunos ejemplos:

  • Comprenda el dominio de su negocio / modelo de datos : igualmente importante si va a diseñar un modelo de objeto o crear una estructura de datos funcional con mapas anidados. De alguna manera, la PF puede ser más fácil porque lo alienta a pensar en el modelo de datos por separado de las funciones / procesos, pero aún tiene que hacer ambas cosas.

  • Orientación del servicio en el diseño : en realidad funciona muy bien desde una perspectiva de PF, ya que un servicio típico es realmente una función con algunos efectos secundarios. Creo que la visión "de abajo hacia arriba" del desarrollo de software que a veces se adhiere en el mundo de Lisp es en realidad solo buenos principios de diseño de API orientados al servicio en otro aspecto.

  • Desarrollo guiado por pruebas : funciona bien en lenguajes de FP, de hecho, a veces incluso mejor porque las funciones puras se prestan extremadamente bien para escribir pruebas claras y repetibles sin necesidad de configurar un entorno de estado. También es posible que desee crear pruebas separadas para verificar la integridad de los datos (por ejemplo, este mapa tiene todas las claves que espero, para equilibrar el hecho de que en un lenguaje OO la definición de clase lo impondría en el momento de la compilación).

  • Prototying / iteration - funciona igual de bien con FP. Incluso podría crear un prototipo en vivo con los usuarios si es extremadamente bueno en la creación de herramientas / DSL y en su uso en el REPL.


Recientemente he encontrado este libro: Modelado de dominios funcional y reactivo

Creo que está perfectamente en línea con su pregunta.

De la descripción del libro:

El modelado de dominios funcional y reactivo le enseña cómo pensar el modelo de dominio en términos de funciones puras y cómo componerlas para construir abstracciones más grandes. Comenzará con los conceptos básicos de la programación funcional y progresará gradualmente hacia los conceptos y patrones avanzados que necesita conocer para implementar modelos de dominio complejos. El libro muestra cómo los patrones avanzados de PF, como los tipos de datos algebraicos, el diseño basado en clase de tipos y el aislamiento de los efectos secundarios, pueden hacer que su modelo se componga para facilitar la lectura y la verificación.



Sinceramente, si desea diseñar recetas para programas funcionales, eche un vistazo a las bibliotecas de funciones estándar, como Prelude de Haskell. En FP, los patrones generalmente son capturados por procedimientos de orden superior (funciones que operan en funciones). Entonces, si se ve un patrón, a menudo se crea una función de orden superior para capturar ese patrón.

Un buen ejemplo es fmap. Esta función toma una función como argumento y la aplica a todos los "elementos" del segundo argumento. Como es parte de la clase de tipo Functor, cualquier instancia de un Functor (como una lista, gráfico, etc.) puede pasarse como un segundo argumento a esta función. Captura el comportamiento general de aplicar una función a cada elemento de su segundo argumento.


Un enfoque es crear un DSL interno dentro del lenguaje de programación funcional que se elija. El "modelo" entonces es un conjunto de reglas de negocios expresadas en el DSL.