Pregunta

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

La nueva exageración 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 sobre Lisp y Clojure, llegué a dos declaraciones interesantes:

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

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

Entonces, ¿cuál es la metodología para un diseño sistemático (basado en modelos?) De una aplicación funcional, es decir, en Lisp o Clojure? ¿Cuáles son los pasos comunes, qué artefactos uso, cómo los mapeo desde el espacio de problemas hasta el espacio de la solución?

¿Fue útil?

Solución

Gracias a Dios que las personas de ingeniería de software aún no han 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 de visitantes se conoce en el mundo funcional como un "pliegue" (o si eres un teórico de cabeza puntual, un "catamorfismo"). En los idiomas funcionales, los tipos de datos son principalmente árboles o tuplas, y cada tipo de árbol tiene un catamorfismo natural asociado con él.

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

  • Los programadores funcionales usan diagramas mucho menos que los programadores OO. En cambio, gran parte de lo que se expresa en los diagramas OO se expresa en tipos, o en "firmas", que debes pensar como "tipos de módulos". Haskell también tiene "clases de tipo", que es un poco como un tipo de interfaz.

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

    No todos los idiomas funcionales usan tipos explícitos, sino el Cómo diseñar programas El libro, un excelente libro para el esquema de aprendizaje/Lisp/Clojure, depende en gran medida de 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 modelos?) 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. Creo que esto es más fácil cuando el lenguaje tiene tipos explícitos, pero funciona incluso sin. 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 Abstracción y especificación en el desarrollo del programa por Barbara Liskov y John Guttag, el primero 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 lenguaje 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 material más general sobre este tipo de diseño buscando el término "lenguaje integrado específico del dominio"; Para obtener consejos y ejemplos particulares más allá de lo que cubre Matthew Flatt, probablemente comenzaría con Graham's En Lisp o quizás Ansi Common Lisp.

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

Pasos comunes:

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

  2. Identifique acciones o patrones comunes de cálculo, y exprese las funciones o macros de orden superior. Espere dar este paso como parte de la refactorización.

  3. Si está utilizando un lenguaje funcional mecanografiado, use el comprobador de tipo temprano y con frecuencia. Si está utilizando LISP o Clojure, la mejor práctica es escribir contratos de funciones primero, incluidas las pruebas unitarias, es el desarrollo basado en 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.

Otros consejos

Para Clojure, recomiendo volver al buen modelado relacional. Fuera de la lona es una lectura inspiradora.

Personalmente, encuentro que todas las buenas prácticas habituales del desarrollo de OO también se aplican en la programación funcional, solo con algunos ajustes menores para tener en cuenta la cosmovisión funcional. Desde una perspectiva de metodología, realmente no necesita hacer nada fundamentalmente diferente.

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

Algunos ejemplos:

  • Comprender el modelo de dominio / datos de su negocio - Igualmente importante si va a diseñar un modelo de objeto o crear una estructura de datos funcionales con mapas anidados. De alguna manera, FP puede ser más fácil porque le anima a pensar en el modelo de datos por separado de las funciones / procesos, pero aún tiene que hacer ambas cosas.

  • Orientación de servicio en diseño - En realidad, funciona muy bien desde una perspectiva FP, ya que un servicio típico es realmente una función con algunos efectos secundarios. Creo que la visión de "abajo" del desarrollo de software a veces adoptada en el mundo de Lisp es en realidad solo buenos principios de diseño de API orientados al servicio con otra apariencia.

  • Desarrollo impulsado por pruebas - Funciona bien en los idiomas de FP, de hecho, a veces incluso mejor porque las funciones puras se prestan extremadamente bien a escribir pruebas claras y repetibles sin necesidad de establecer un entorno con 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, equilibrar el hecho de que en un lenguaje OO la definición de clase lo impediría en el momento de la compilación).

  • Prototor / iteración - Funciona igual de bien con FP. Incluso puede ser capaz de prototipo en vivo con los usuarios si se vuelve muy bueno para construir herramientas / DSL y usarlas en el repl.

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

Por lo tanto, no hay libros y libros al respecto, pero hay un enfoque bien establecido para, como dice el dicho, que los valores imposibles no sean representables.

Al hacerlo, puede tomar una variedad de decisiones sobre la representación de ciertos tipos de datos como funciones en su lugar y, por el contrario, 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 ADT de modo que establezca algún tipo de álgebra - es decir, hay leyes fijas que tienen para estas funciones. Algunos son quizás ideMpotent, 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 componen de acuerdo con las leyes bien comportadas. ¡Un simple DSL incrustado!

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

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 de trabajo, a tiempo y con una baja tasa de defectos. La programación funcional puede ser diferente de OO, pero no elimina los requisitos, diseños de alto nivel y detallados, verificación y pruebas, métricas de software, estimación y todas esas otras "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.

Un enfoque es crear un DSL interno dentro del lenguaje de programación funcional de elección. El "modelo" es un conjunto de reglas comerciales expresadas en el DSL.

Vea mi respuesta a otra publicación:

¿Cómo se separa las preocupaciones de Clojure Aproach?

Estoy de acuerdo en que se necesita escribir más sobre el tema sobre cómo estructurar grandes aplicaciones que usen un enfoque FP (además de que se debe hacer más para documentar UI basadas en FP)

Si bien esto podría considerarse ingenuo y simplista, creo que las "recetas de diseño" (un enfoque sistemático para la resolución de problemas aplicado a la programación según lo defienden Felleisen et al. En su libro HTDP) estaría cerca de lo que parece estar buscando.

Aquí, algunos enlaces:

http://www.northeastern.edu/magazine/0301/programming.html

http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.86.8371

He descubierto que el desarrollo del comportamiento es un ajuste natural para el código de desarrollo rápido tanto en Clojure como en SBCL. El verdadero alza de aprovechar BDD con un lenguaje funcional es que tiendo a escribir pruebas unitarias de granos más finas de lo que generalmente uso en los idiomas de procedimiento porque hago un trabajo mucho mejor al descomponer el problema en fragmentos de funcionalidad más pequeños.

Recientemente encontré este libro:Modelado de dominio funcional y reactivo

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

De la descripción del libro:

El modelado de dominio funcional y reactivo le enseña cómo pensar en 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 a los conceptos y patrones avanzados que necesita saber para implementar modelos de dominio complejos. El libro demuestra cómo los patrones de FP avanzados como los tipos de datos algebraicos, el diseño basado en tipos de mecanografía y el aislamiento de los efectos secundarios pueden hacer que su modelo componga la legibilidad y la verificabilidad.

Honestamente, si desea recetas de diseño para programas funcionales, eche un vistazo a las bibliotecas de funciones estándar como el preludio de Haskell. En FP, los patrones generalmente son capturados por procedimientos de orden superior (funciones que operan en funciones) mismos. Entonces, si se ve un patrón, a menudo se crea una función de orden superior simplemente 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. Dado que es parte de la clase de tipo de functores, cualquier instancia de un functor (como una lista, gráfico, etc.) puede pasar como un segundo argumento a esta función. Captura el comportamiento general de aplicar una función a cada elemento de su segundo argumento.

Existe el estilo "Cálculo del programa" / "Diseño por cálculo" asociado con el Prof. Richard Bird y el álgebra del Grupo de Programación en la Universidad de Oxford (Reino Unido), no creo que sea demasiado descabellado para 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 yo mismo. Sin embargo, esa es mi deficiencia, y no uno de los cálculos del programa.

Bien,

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

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

Estoy seguro de que, cuando los chicos de Functioanl llegan a la pared [intente implementar sistemas más de 1.000,000 líneas de código], algunos de ellos vendrán con nuevas metodologías de ingeniería de software con palabras de moda :-). Deberían responder a la vieja pregunta: ¿cómo dividir el sistema en pedazos para que podamos "morder" cada pieza una a la vez? [Trabajo iterativo, inceremental en forma evolutiva] utilizando estilo funcional.

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

Pero, ¿se utilizarán los programas funcionales para un sistema tan grande? ¿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, ensuciando sus manos. Primero, debe ensuciarse las manos y luego sugerir una solución. Solutions-Hugestions sin "dolores reales y suciedad" será "fantasía".

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top