¿Existe una metodología de ingeniería de software para la programación funcional? [cerrado]

203

La ingeniería de software, tal como se enseña hoy, se centra por completo 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 muchos 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 sobre Lisp y Clojure, descubrí dos declaraciones interesantes:

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

  2. Los programadores funcionales usan mapas donde los programadores 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 el modelo) de una aplicación funcional, es decir, en Lisp o Clojure? ¿Cuáles son los pasos comunes, qué artefactos utilizo, cómo los mapeo desde el espacio del problema al espacio de la solución?

Thorsten
fuente
3
Tengo un comentario aquí: muchos programas están escritos de arriba hacia abajo, una exposición práctica del proceso de desarrollo de software en un lenguaje funcional se da en el libro "Programación funcional en limpieza concurrente" (el lenguaje en sí es muy académico, aunque).
Artyom Shalkhakov 01 de
44
1. Parnas argumenta que la mayoría de los programas deberían ser de abajo hacia arriba y luego fingidos para parecer de arriba hacia abajo, por lo que esos enfoques deberían ser mixtos, no hay una respuesta correcta.
Gabriel Ščerbák 01 de
2
2. Los objetos proporcionan comportamiento dependiendo de su estado estructurado encapsulado, en FP tiene todos los estados y estructuras explícitos y el comportamiento (funciones) está separado de la estructura. Entonces, para el modelado de datos, se usan mapas para objetos, pero al diseñar aplicaciones, los objetos no se pueden reemplazar con funciones: FP es una gran expresión generada y evaluada a través de tuberías, OOP se trata de crear el modelo y enviar mensajes entre objetos.
Gabriel Ščerbák
1
En algún momento hice una pregunta relacionada: "¿cómo se modelan los datos de las bases de datos relacionales en clojure?" stackoverflow.com/questions/3067261/…
Sandeep
44
Jeje, en una de las conferencias del SICP, Hal Abelson dice, medio en broma, algo así como "Hay una metodología famosa, o debería decir mitología, llamada ingeniería de software que hace diagramas y requisitos complicados y luego construye sistemas con ellos; esas personas no han programado mucho ". Vengo de una "Escuela Java", donde durante años enseñamos UML y artefactos y cosas, y aunque un poco es bueno, demasiada planificación y planificación (juego de palabras) es más dañino que útil: nunca se sabe cómo el software estará hasta que llegue a codificar realmente.
lfborjas

Respuestas:

165

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" OO se capturan como funciones de orden superior. Por ejemplo, el patrón Visitor se conoce en el mundo funcional como un "pliegue" (o si eres un teórico puntiagudo, un "catamorfismo"). En lenguajes funcionales, los tipos de datos son principalmente á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 usan 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 debe considerar como "tipos de módulos". Haskell también tiene "clases de tipo", que es un poco como un tipo de interfaz.

    Esos programadores funcionales que usan tipos generalmente piensan que "una vez que obtienes los tipos correctos, el código prácticamente se escribe solo".

    No todos los lenguajes funcionales usan tipos explícitos, pero el libro Cómo diseñar programas , un excelente libro para aprender Scheme / Lisp / Clojure, depende en gran medida de "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 el 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. 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 Abstracción y especificación en el desarrollo de programas 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 única para 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 Creando 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 incrustado 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 On Lisp o quizás 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 o patrones comunes de cálculo y expréselos como funciones de orden superior o macros. Espere 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 el desarrollo basado en pruebas al máximo. Y querrá usar cualquier versión de QuickCheck que haya sido portada 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.

Norman Ramsey
fuente
2
El visitante de la OMI no se pliega: el pliegue es un subconjunto de visitantes. El despacho múltiple no se captura (directamente) por plegado.
Michael Ekstrand
66
@Michael: en realidad, puedes capturar despachos múltiples con varios tipos de catamorfismos de orden superior de forma muy clara. El trabajo de Jeremy Gibbons es un lugar para buscar esto, pero recomendaría trabajar en programación genérica de tipo de datos en general: me gusta especialmente el papel compuesto.
sclv
66
Estoy de acuerdo en que veo diagramas utilizados con mucha menos frecuencia para describir diseños funcionales y creo que es una pena. Es ciertamente difícil representar el equivalente de un diagrama de secuencia cuando se usa mucho HOF. Pero desearía que se explorara mejor el espacio de cómo describir diseños funcionales con imágenes. Por mucho que odie UML (como especificación), encuentro que UML (como boceto) es bastante útil en Java y desearía que hubiera mejores prácticas sobre cómo hacer el equivalente. He estado experimentando un poco haciendo esto con los protocolos y registros de Clojure, pero no tengo nada que realmente me guste.
Alex Miller
22
+1 por "Gracias a Dios que la gente de ingeniería de software aún no ha descubierto la programación funcional". ;)
Aky
1
OO es en sí mismo una forma de tratar de programar con tipos, por lo que los enfoques no son tan diferentes. El problema con los diseños OO generalmente parece derivarse de que las personas no saben lo que están haciendo.
Marcin
46

Para Clojure, recomiendo volver a los viejos modelos relacionales. Out of the Tarpit es una lectura inspiradora.

cgrand
fuente
Es un gran artículo, los buenos viejos tiempos en Ciencias de la Computación deben haber sido realmente impresionantes, cuando todos estos conceptos sobrevivieron hasta el renacimiento de hoy. Probablemente se deba a los sólidos cimientos de las matemáticas.
Thorsten
1
Esta. ESTA. ¡ESTA! Estoy leyendo este documento, y es realmente interesante cómo parece cubrir todas las bases de lo que se necesita para construir sistemas reales, al tiempo que mantiene un estado mutable mínimo de una manera altamente controlada. Estoy jugando con la construcción de Pong y Tetris en un estilo FRelP (disculpe el extraño inicialismo, pero ya hay otro FRP más popular: la programación funcional reactiva).
John Cromartie
Después de leer el artículo, creo que clojure sería el lenguaje perfecto para FR (el) P, al menos para la lógica esencial , el estado accidental y el control y los otros componentes. Me pregunto cómo hacer una definición relacional del estado esencial en clojure sin reinventar sql (sin sus defectos). ¿O es la idea simplemente usar una buena base de datos relacional (sql) y construir un programa funcional además de la falta de coincidencia conceptual introducida por OOP?
Thorsten
1
@Thorsten la idea básica es set = table, map = index. La parte difícil es mantener sincronizados los índices y las tablas, pero este problema se puede resolver con mejores tipos de conjuntos. Un tipo de conjunto simple que implementé es el conjunto de claves, que es un conjunto que utiliza una función clave para probar la unicidad. Esto significa que conjurar una inserción o actualización de valor, llamar a get con los campos de clave primaria devuelve toda la fila.
cgrand
38

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

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

Algunos ejemplos:

  • Comprenda su dominio comercial / modelo de datos , igualmente importante si va a diseñar un modelo de objetos o crear una estructura de datos funcional con mapas anidados. De alguna manera, FP puede ser más fácil porque lo alienta a pensar sobre 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 FP, ya que un servicio típico es realmente solo una función con algunos efectos secundarios. Creo que la visión "de abajo hacia arriba" del desarrollo de software, a veces adoptada en el mundo de Lisp, en realidad es solo un buen principio de diseño de API orientado al servicio en otro aspecto.

  • Desarrollo guiado por pruebas : funciona bien en lenguajes 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 con estado. También es posible que desee crear pruebas separadas para verificar la integridad de los datos (p. Ej., Este mapa tiene todas las claves que espero, para equilibrar el hecho de que en un lenguaje OO la definición de clase lo aplicaría en el momento de la compilación).

  • Creación de prototipos / iteración : funciona igual de bien con FP. Incluso podría crear prototipos en vivo con los usuarios si se vuelve extremadamente bueno construyendo herramientas / DSL y usándolas en REPL.

mikera
fuente
3
Estas prácticas me suenan bastante familiares. Sigo pensando que alguien debería escribir el equivalente funcional a "Ingeniería de software orientada a objetos usando UML, patrones y Java" de Bruegge / Dutoit en lugar del sexto libro "Programación en Clojure". Podría llamarse "Ingeniería de software funcional utilizando Clojure y" ¿qué? ". ¿Usan UML y patrones en FP? Recuerdo que Paul Graham escribió que los patrones son una señal de falta de abstracción en Lisp, que debería remediarse con la introducción de nuevas macros.
Thorsten
3
Pero si traduce patrones como mejores prácticas, también puede haber patrones en el mundo de la PF, que vale la pena compartir con los no iniciados.
Thorsten
2
Hay algunos diseños de principios interesantes en el libro PAIP. norvig.com/paip.html
mathk
1
también hay patrones de programación funcionales (esquemas de recursividad, etc.)
Gabriel Ščerbák
13

La programación OO combina estrechamente los datos con el comportamiento. La programación funcional separa a los dos. Por lo tanto, no tiene diagramas de clases, 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.

Entonces, no hay libros y libros sobre él, pero hay un enfoque bien establecido para, como dice el dicho, hacer que los valores imposibles sean irrepresentables.

Al hacerlo, puede hacer una variedad de elecciones 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 que, escribe funciones sobre sus anuncios de modo que establezca algún tipo de álgebra , es decir, hay leyes fijas que se cumplen para 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 las leyes de buen comportamiento. ¡Un simple DSL incrustado!

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

sclv
fuente
1
El enfoque de hacer que los valores imposibles sean irrepresentables es menos aplicable a los idiomas con escritura dinámica como Clojure y Scheme que a los idiomas con escritura estática como Haskell y ML.
Zak
@Zak: bueno, no puedes comprobar estáticamente que no son representables, pero puedes construir tus estructuras de datos de la misma manera de todos modos.
sclv
7

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 la OO, pero no elimina los requisitos, los diseños detallados y de alto nivel, la verificación y las pruebas, las métricas de software, la 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 de esa estructura.

Kaz
fuente
5

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.

James Kingsbery
fuente
1
Entiendo el enfoque para construir primero el lenguaje hacia el dominio del problema hasta que se alcance un nivel de abstracción en el que ya no ocurran patrones repetitivos en el código, que resolver el problema con esas abstracciones.
Thorsten
1
Pero, ¿cómo se ve cuando "el modelo es un conjunto de reglas comerciales expresadas en el DSL"? En una aplicación Java EE, el modelo se escribe como entidades POJO, que se llaman desde los EJB del controlador que a su vez actualizan las JSP de vista, por ejemplo. ¿Hay patrones arquitectónicos similares (como el patrón MVC) en FP? ¿Cómo se ve eso?
Thorsten
2
No hay ninguna razón por la que no pueda tener un patrón MVC en FP, precisamente así. FP aún le permite construir estructuras de datos enriquecidas, y posiblemente con ADT y coincidencia de patrones, le permite construir estructuras mucho más ricas . En todo caso, dado que FP separa los datos y el comportamiento, los sistemas de tipo MVC surgen mucho más naturalmente.
sclv 02 de
5

Vea mi respuesta a otra publicación:

¿Cómo aborda Clojure la separación de las preocupaciones?

Estoy de acuerdo en que se necesita escribir más sobre el tema sobre cómo estructurar aplicaciones grandes que usan un enfoque de FP (Además, se necesita hacer más para documentar las UI impulsadas por FP)

drcode
fuente
3
Me gusta la tubería del 90% y el enfoque macro del 10%. Parece bastante natural pensar en un programa funcional como una tubería de transformaciones en datos inmutables. No estoy seguro si entiendo lo que quieres decir con "poner toda la inteligencia en los datos, no en el código", ya que el enfoque de tener 100 funciones trabajando en 1 estructura de datos (en lugar de 10 funciones en 10 estructuras de datos) parece implicar lo contrario. ¿No son las estructuras de datos en OOP más inteligentes que en FP, ya que tienen su propio comportamiento incorporado?
Thorsten
3

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 como lo recomiendan 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

Artyom Shalkhakov
fuente
El enlace a la página Noreste parece estar muerto.
James Kingsbery
1
James, tienes razón, y no recuerdo qué había allí para arreglarlo, desafortunadamente. Solo sé que los autores de HtDP crearon el lenguaje Pyret (y probablemente están revisando la segunda edición de HtDP para usarlo en lugar de Racket, anteriormente PLT Scheme).
Artyom Shalkhakov
3

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 avanzados de FP como los tipos de datos algebraicos, el diseño basado en la clase de tipo y el aislamiento de los efectos secundarios pueden hacer que su modelo componga la legibilidad y la verificabilidad.

elviejo79
fuente
2

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 de la Universidad de Oxford (Reino Unido), no creo que sea demasiado descabellado considerar esta 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, esa es mi deficiencia, y no una de cálculo de programa.

Stephen tetley
fuente
2

He descubierto que Behavior Driven Development es una opción natural para desarrollar código rápidamente tanto en Clojure como en SBCL. La verdadera ventaja de aprovechar BDD con un lenguaje funcional es que tiendo a escribir pruebas unitarias de grano mucho más finas de lo que suelo hacer cuando uso lenguajes de procedimiento porque hago un trabajo mucho mejor descomponiendo el problema en trozos más pequeños de funcionalidad.

Bagazo
fuente
¿Cuáles son las herramientas que está utilizando para hacer BDD en clojure?
murtaza52
Me gusta Midje Está actualizado y es muy expresivo. Compruébalo: github.com/marick/Midje
Marc
1

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) ellos mismos. Entonces, si se ve un patrón, a menudo se crea simplemente 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 para esta función. Captura el comportamiento general de aplicar una función a cada elemento de su segundo argumento.

nightski
fuente
-7

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 paralela" debido al "estado". Y a veces el estilo funcional es mejor para problemas como Google MapReduce.

Estoy seguro de que, cuando los muchachos funcionales golpeen la pared [intente 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 :-). Deberían responder a la vieja pregunta: ¿Cómo dividir el sistema en piezas para que podamos "morder" cada pieza una a la vez? [trabajo iterativo, inceremental en forma evolutiva] usando el estilo funcional.

Es seguro que el Estilo Funcional afectará nuestro Estilo Orientado a Objetos. "Todavía" muchos conceptos de Sistemas Funcionales y adaptados a nuestros lenguajes OOP.

Pero, ¿se utilizarán programas funcionales para sistemas tan grandes? ¿Se convertirán en una 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 debes ensuciarte las manos y luego sugerir una solución. Soluciones-Sugerencias sin "dolores y suciedad reales" serán "fantasía".

Hipias Menores
fuente
Se han construido suficientes sistemas a gran escala con lenguajes funcionales ahora. Incluso si no lo hubiera hecho, este no es un argumento en absoluto.
Svante
Bueno, nombrar algunos de ellos? Solo conozco muy pocos sistemas "Erlang". [tamaño mediano] ¿Pero Haskel? Clojure? ¿Ceceo?
Hippias Minor
Y eso [escribir grandes sistemas] es el argumento real. Porque ese es el caso de prueba. Este caso de prueba muestra que si este estilo funcional es útil y podemos hacer cosas prácticas con él en el mundo real.
Hippias Minor
2
Lo curioso de los idiomas que no son analmente "OOP" es que a menudo te liberan de las "metodologías de diseño", de pensar por ti mismo y de cortar tu programa de la manera más apropiada, en lugar de seguir ciegamente un patrón establecido y vivir con el repetitivo burocrático. Lo sentimos, no hay curso de 10 puntos y 3 semanas aquí.
Svante
1
He visto cosas que no creerías.
Svante