434 votos

¿Diseño a gran escala en Haskell?

Lo que es una buena forma de diseño/estructura de grandes programas funcionales, especialmente en Haskell?

He sido a través de un montón de tutoriales (Escribir un Esquema siendo mi favorito, con el Mundo Real Haskell, un cercano segundo lugar), pero la mayoría de los programas son relativamente pequeños, y de un solo propósito. Además, no considero que algunos de ellos sean muy elegante (por ejemplo, la gran tablas de búsqueda en WYAS).

Ahora estoy con ganas de escribir programas más grandes, con más partes móviles de adquisición de datos de una variedad de diferentes fuentes, la limpieza, el procesamiento de diversas maneras, mostrando que en las interfaces de usuario, la persistencia, la comunicación a través de redes, etc. ¿Cómo podría una mejor estructura de un código legible, fácil de mantener, y adaptable a las necesidades cambiantes?

Hay una gran cantidad de literatura abordar estas preguntas para grandes orientada a objetos imperativo programas. Ideas como MVC, patrones de diseño, etc. son decentes recetas para alcanzar metas amplias, como la separación de las preocupaciones y la reutilización en un estilo OO. Además, los nuevos lenguajes imperativos se prestan a un diseño " a medida que crece el estilo de refactorización para que, en mi novato opinión, Haskell aparece menos adecuado.

¿Hay un equivalente en la literatura para Haskell? ¿Cómo es el zoológico de exóticas estructuras de control disponibles en la programación funcional (mónadas, flechas, aplicativo, etc.) mejor emplear para este propósito? Lo mejor de las prácticas recomiendan?

Gracias!

EDITAR (esto es un seguimiento a Don Stewart respuesta):

@dons menciona: "las Mónadas de captura de la clave de los diseños arquitectónicos de tipos".

Supongo que mi pregunta es: ¿cómo se debe pensar acerca de la clave de los diseños arquitectónicos de un lenguaje funcional puro?

Considere el ejemplo de varios flujos de datos, y varios pasos de procesamiento. Puedo escribir modular analizadores de los flujos de datos a un conjunto de estructuras de datos, y me puede implementar cada paso de procesamiento como una pura función. El procesamiento de los pasos necesarios para una pieza de datos dependerá de su valor y de los demás. Algunos de los pasos que deben ser seguidos por sus efectos secundarios interfaz gráfica de usuario de actualizaciones o consultas de base de datos.

¿Cuál es el " Derecho " a la manera de atar los datos y el análisis de los pasos de una buena manera? Uno podría escribir una gran función que hace lo correcto para los distintos tipos de datos. O también se podría utilizar una monada de seguir la pista de lo que ha sido procesado hasta la fecha y en cada paso del procesamiento de conseguir lo que necesita siguiente de la mónada estado. O uno podría escribir en gran medida independientes de los programas y enviar mensajes a su alrededor (no me gusta mucho esta opción).

Las diapositivas se han vinculado una de las Cosas que Necesitamos de la viñeta: "Modismos para la asignación de diseño en tipos y funciones de los/clases/mónadas". ¿Cuáles son los modismos? :)

400voto

Don Stewart Puntos 94361

Puedo hablar un poco acerca de esto en la Ingeniería de Grandes Proyectos en Haskell y en el Diseño e Implementación de XMonad. La ingeniería en la grande es acerca de la gestión de la complejidad. El código principal la estructuración de mecanismos en Haskell para la gestión de la complejidad son :

El tipo de sistema de

  • Utilice el tipo de sistema para aplicar las abstracciones, la simplificación de las interacciones.
  • Hacer cumplir clave invariantes a través de los tipos de
    • (por ej. que ciertos valores no pueden escapar algunos ámbito de aplicación)
    • Que cierto código no IO, no toca el disco
  • Hacer cumplir la seguridad: excepciones comprobadas (tal vez/), evitar la mezcla de conceptos (Word,Int,Dirección)
  • Bueno estructuras de datos (como cremalleras) puede hacer que algunas clases de pruebas innecesarias, ya que descartar por ejemplo. fuera de los límites de los errores de forma estática.

El analizador de

  • Proveer evidencia objetiva de su montón de programas y tiempo de perfiles.
  • Montón de perfiles, en particular, es la mejor manera de asegurarnos de que no uneccessary uso de la memoria.

La pureza

  • Reducir drásticamente la complejidad mediante la eliminación del estado. Puramente funcional código escalas, porque es la composición. Todo lo que usted necesita es el tipo para determinar cómo utilizar el código -- no, misteriosamente, se rompen cuando se cambia alguna otra parte del programa.
  • El uso de un montón de "modelo/vista/controlador de" estilo de programación: analizar datos externos tan pronto como sea posible dentro de la puramente funcional, estructuras de datos, operar en esas estructuras, a continuación, una vez que todo el trabajo está hecho, render/ras/serializar. Mantiene la mayoría de su código puro

De prueba

  • QuickCheck + Haskell Cobertura de Código, para asegurarse de que están probando las cosas que no se pueden comprobar con los tipos.
  • GHC +RTS es genial para ver si está gastando demasiado tiempo haciendo GC.
  • QuickCheck también puede ayudar a identificar limpio, ortogonal Api para los módulos. Si las propiedades de su código son difíciles de estado, son probablemente demasiado complejo. Mantener la refactorización hasta que tienes una serie de propiedades que pueden probar el código, que la componen. A continuación, el código es, probablemente, bien diseñadas también.

Las mónadas para la Estructuración de

  • Las mónadas de captura de la clave de los diseños arquitectónicos de tipos (en este código accesos de hardware, este código es de una sola sesión de usuario, etc .)
  • Por ejemplo. la X mónada en xmonad, captura, precisamente, el diseño de lo que el estado es visible a qué componentes del sistema.

Las clases de tipo existencial y tipos

  • El tipo de uso de clases para proporcionar una abstracción: ocultar las implementaciones detrás de polimórficos interfaces.

La concurrencia y paralleism

  • Sneak par en su programa para ganar a la competencia fácil, que se puede componer el paralelismo.

Refactorizar

  • Puede refactorizar en Haskell mucho. Los tipos de garantizar que sus cambios a gran escala va a estar seguro, si usted está usando tipos sabiamente. Esto ayudará a su base de escala. Asegúrese de que su refactorings le causa errores de tipo hasta que se completa.

El uso de la FFI sabiamente

  • La FFI hace que sea más fácil jugar con extranjeros, el código, pero que los extranjeros código puede ser peligroso.
  • Ser muy cuidadoso en las suposiciones acerca de la forma de los datos devueltos.

Meta de programación

  • Un poco de Plantilla de Haskell o genéricos pueden quitar repetitivo.

Embalaje y distribución

  • El Uso De La Cábala. No darse su propio sistema de construcción.
  • Uso Haddock para el bien de la API de google docs
  • Herramientas como graphmod puede mostrar su módulo de estructuras.
  • Se basan en el Haskell versiones de la Plataforma de bibliotecas y herramientas, si es posible. Es una base estable.

Advertencias

  • Uso -Wall para mantener el código limpio de olores. También puede buscar en el Agda, Isabelle o de Captura para mayor seguridad. Para pelusas como el control, ver la gran hlint, que se sugieren mejoras.

Con todas estas herramientas puede tener un mango en complejidad, como la eliminación de muchas de las interacciones entre los componentes como sea posible. Idealmente, usted tiene una gran base de código puro, que es realmente fácil de mantener, ya que es la composición. Eso no siempre es posible, pero vale la pena objetivo.

En general: descomponer las unidades lógicas de su sistema en el menor referentially componentes transparentes posible, aplicar en los módulos. Local o Global de los entornos para los conjuntos de componentes (o en el interior de los componentes) pueden asignarse a las mónadas. Uso algebraica de tipos de datos para describir las estructuras de datos principales. Compartir esas definiciones ampliamente.

88voto

user349653 Puntos 681

No le dio la mayoría de los detalles anteriormente mencionados, pero he aquí mis dos centavos de hacer realmente nitty-gritty el estado de programas, como el de los demonios del sistema en Haskell.

  1. Al final, usted vive en una mónada transformador de la pila. En la parte inferior es IO. Por encima de eso, cada módulo (en el sentido abstracto, no en el módulo-en-un-archivo sentido) mapas de su estado necesarias en una capa en la pila. Así que si usted tiene su conexión de base de datos de código oculto en un módulo, se escribe todos a ser más de un tipo de MonadReader Conexión m => ... -> m ... y, a continuación, sus funciones de base de datos siempre se puede conseguir la conexión, sin funciones de otros módulos de tener que ser consciente de su existencia. Usted podría terminar con una capa de transporte de su conexión de base de datos, otra de su configuración, de un tercio de sus varios semáforos y mvars para la resolución de paralelismo y de la sincronización, la otra su registro de identificadores de archivo, etc.

  2. Averiguar el control error de la primera. La mayor debilidad en el momento de Haskell en los sistemas más grandes, es la gran cantidad de métodos de control de errores, incluyendo el mal, como tal vez (lo cual es incorrecto porque no se le puede devolver cualquier información sobre lo que salió mal; siempre usar ya Sea en vez de a menos que realmente significa sólo la falta de valores). Averiguar cómo le vamos a hacer primero, y configurar los adaptadores de los diversos error de los mecanismos de control de sus bibliotecas y otros de código se utiliza en su final. Esto le ahorrará un mundo de dolor más adelante.

31voto

augustss Puntos 402

El diseño de los programas de gran tamaño en Haskell no es muy diferente de hacerlo en otros idiomas. La programación en la grande se trata de romper el problema en partes manejables, y cómo encajan esos juntos; el lenguaje de implementación es menos importante.

Dicho esto, en un diseño de gran tamaño es bueno tratar de aprovechar el tipo de sistema para asegurarse de que sólo puede encajar sus piezas se unen de una manera que es correcta. Esto puede implicar newtype o fantasma tipos para hacer las cosas que parecen tener el mismo tipo de ser diferente.

Cuando se trata de la refactorización del código a medida que avanza, la pureza es una gran ventaja, así que trate de mantener todo el código posible puro. Puro código es fácil para refactorizar, porque no tiene oculto de la interacción con otras partes de su programa.

15voto

Neil Mitchell Puntos 2510

Escribí un poco sobre esto bajo "Directrices de diseño" en este documento:

http://Community.Haskell.org/~NDM/downloads/Paper-hoogle_overview-19_nov_2008.pdf

11voto

comonad Puntos 1852

Me hizo aprender estructurados de programación funcional el primer tiempo con este libro. Puede ser que no sea exactamente lo que está buscando, pero para los principiantes en programación funcional, este puede ser uno de los mejores primeros pasos para aprender a estructura funcional programas - independiente de la escala. En todos los niveles de abstracción, el diseño siempre debe tener claro estructuras.

El arte de la Programación Funcional

The Craft of Functional Programming

http://www.cs.kent.ac.uk/people/staff/sjt/craft2e/

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X