diseño de grandes proyectos en OCaml


¿Cuáles son las mejores prácticas para escribir grandes proyectos de software en OCaml?

¿Cómo estructuran sus proyectos?

¿Qué características de OCaml deben y no deben usarse para simplificar la administración de código? Las excepciones? ¿Módulos de primera clase? ¿GADTs? ¿Tipos de objetos?

¿Sistema de construcción? Marco de pruebas? ¿Pila de biblioteca?

Encontré excelentes recomendaciones para haskell y creo que sería bueno tener algo similar para OCaml.

Author: Community, 2013-10-14

3 answers

Voy a responder por un proyecto de tamaño mediano en las condiciones con las que estoy familiarizado, es decir, entre 100K y 1M de líneas de código fuente y hasta 10 desarrolladores. Esto es lo que estamos usando ahora, para un proyecto que comenzó hace dos meses en agosto de 2013.

Sistema de compilación y organización del código:

  • un script de shell compatible con el código fuente define la ruta y otras variables para nuestro proyecto
  • uno .el archivo ocamlinit en la raíz de nuestro proyecto carga un montón de bibliotecas al iniciar un sesión de nivel superior
  • omake, que es rápido (con la opción-j para compilaciones paralelas); pero evitamos hacer complementos omake personalizados locos
  • un Makefile raíz contiene todos los destinos esenciales (setup, build, test, clean y más)
  • un nivel de subdirectorios, no dos
  • la mayoría de los subdirectorios se construyen en una biblioteca OCaml
  • algunos subdirectorios contienen otras cosas (setup, scripts, etc.)
  • OCAMLPATH contiene la raíz del proyecto; cada subdirectorio de biblioteca produce un archivo META, haciendo que todas las partes OCaml de los proyectos sean accesibles desde el nivel superior usando # require.
  • solo se construye un ejecutable OCaml para todo el proyecto (ahorra mucho tiempo de enlace; todavía no se sabe por qué)
  • las bibliotecas se instalan a través de un script de instalación utilizando opam
  • los paquetes opam locales están hechos para software que no está en el repositorio opam oficial
  • utilizamos un conmutador opam que es un alias que lleva el nombre de nuestro proyecto, evitando conflictos con otros proyectos en la misma máquina

Edición del código fuente:

  • emacs con paquetes opam ocp-indent y ocp-index

Control y gestión de fuentes:

  • usamos git y github
  • todo el código nuevo es revisado por pares a través de solicitudes de extracción de github
  • los tarballs para bibliotecas que no son opam y que no son github se almacenan en un repositorio git separado (que puede desaparecer si el historial se vuelve demasiado grande)
  • las bibliotecas de vanguardia existentes en github se bifurcan en nuestro github cuenta e instalado a través de nuestro propio paquete local de opam

Uso de OCaml:

  • OCaml no compensará las malas prácticas de programación; enseñar el buen gusto está más allá del alcance de esta respuesta. http://ocaml.org/learn/tutorials/guidelines.html es un buen punto de partida.
  • OCaml 4.01.0 hace que sea mucho más fácil que antes reutilizar etiquetas de campos de registro y constructores de variantes (es decir, type t1 = {x:int} type t2 = {x:int;y:int} let t1_of_t2 ({x}:t2) : t1 = {x} ahora funciona)
  • tratamos de no usar extensiones de sintaxis camlp4 en nuestro propio código
  • no usamos clases y objetos a menos que sea requerido por alguna biblioteca externa
  • en teoría desde OCaml 4.01.0 deberíamos preferir variantes clásicas sobre variantes polimórficas
  • usamos excepciones para indicar errores y los dejamos pasar felizmente hasta que nuestro bucle del servidor principal los atrapa y los interpreta como "error interno" (predeterminado), "solicitud incorrecta" o algo más
  • las excepciones como Exit o Not_found se pueden usar localmente cuando tiene sentido, pero en el módulo interfaces preferimos usar opciones.

Bibliotecas, protocolos, frameworks:

  • usamos baterías para todas las funciones de commodity que faltan en la biblioteca estándar de OCaml; para el resto tenemos una biblioteca "util"
  • usamos Lwt para la programación asíncrona, sin las extensiones de sintaxis, y el operador bind (>>=) es el único operador que usamos (si tiene que saberlo, usamos a regañadientes el preprocesamiento camlp4 para un mejor seguimiento de excepciones en bind punto).
  • utilizamos HTTP y JSON para comunicarnos con software de terceros y esperamos que todos los servicios modernos proporcionen dichas API
  • para servir HTTP, ejecutamos nuestro propio servidor SCGI (ocaml-scgi) detrás de nginx
  • como cliente HTTP utilizamos Cohttp
  • para la serialización JSON usamos atdgen

Servicios"en la nube":

  • usamos muchos de ellos, ya que generalmente son baratos, fáciles de interactuar y resuelven problemas de escalabilidad y mantenimiento para nos.

Pruebas:

  • tenemos un objetivo make / omake para pruebas rápidas y otro para pruebas lentas
  • las pruebas rápidas son pruebas unitarias; cada módulo puede proporcionar una función de "prueba"; a test.ml el archivo ejecuta la lista de pruebas
  • las pruebas lentas son aquellas que implican ejecutar múltiples servicios; estos están diseñados específicamente para nuestro proyecto, pero cubren tanto como sea posible como un servicio de producción. Todo se ejecuta localmente en Linux o macOS, excepto para los servicios en la nube para los que encontramos maneras de no interferir con la producción.

Configurar todo esto es bastante trabajo, especialmente para alguien que no está familiarizado con OCaml. Todavía no hay un framework que se ocupe de todo eso, pero al menos tienes la opción de las herramientas.

 57
Author: Martin Jambon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-13 14:53:18

OASIS

Para añadir a la respuesta de Pavel:

Descargo de responsabilidad: Soy el autor de OASIS.

OASIS también tiene oasis2opam que puede ayudar a crear paquetes OPAM rápidamente y oasis2debian para crear paquetes Debian. Esto es extremadamente útil si desea crear un destino 'release' que automatice la mayoría de las tareas para cargar un paquete.

OASIS también se envía con un script llamado oasis-dist.ml eso crea automáticamente un tarball para subir.

Mira todo esto en https://github.com/ocaml.org .

Pruebas

Uso OUnit para hacer todas mis pruebas. Esto es simple y bastante eficiente si está acostumbrado a las pruebas de xUnit.

Control/gestión de fuentes

Descargo de responsabilidad: Soy el propietario/mantenedor de forge.ocamlcore.org (alias forge.o. o)

Si quieres usar git, te recomiendo usar github. Esto es realmente eficiente para la revisión.

Si usa darcs o subversion, puede crear una cuenta en forjar.o. o.

En ambos casos, tener una lista de correo pública donde enviar todas las notificaciones de confirmación es una necesidad, para que todos puedan verlas y revisarlas. Puede usar grupos de Google o una lista de correo en forge.o. o.

Recomiendo tener una buena web (github o forja.o. o) página con documentación OCamldoc construir cada vez que se confirma. Si tiene una base de código enorme, esto le ayudará a usar la documentación generada por OCamldoc desde el principio (y arreglarla pronto).

Recomiendo crear tarballs cuando llegues a una etapa estable. No se limite a comprobar la última versión de git/svn. Este consejo me ha ahorrado horas de trabajo en el pasado. Como dijo Martin, almacene todos sus archivos tarballs en un lugar central (un repositorio git es una buena idea para eso).

 10
Author: gildor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-14 22:51:39

Este probablemente no responda a su pregunta por completo, pero aquí está mi experiencia con respecto al entorno de construcción:

Realmente aprecio OASIS. Tiene un buen conjunto de características, ayudando no solo a construir el proyecto, sino también a escribir documentación y apoyar el entorno de prueba.

Sistema de construcción

  • OASIS genera el archivo setup.ml a partir del archivo de especificación (_oasis), que funciona básicamente como un script de construcción. Acepta -configure, -build, -test, -distclean banderas. Me acostumbré a ellos mientras trabajaba con diferentes GNU y otros proyectos que usualmente usan Makefiles y me parece conveniente que es posible usarlos todos automáticamente aquí.
  • Makefiles. En lugar de generar setup.ml, también es posible generar Makefile con todas las opciones descritas anteriormente disponibles.

Estructura

Normalmente mi proyecto que es construido por OASIS tiene al menos tres directorios: src, _build, scripts y tests.

  • En el directorio anterior, todos los archivos fuente se almacenan en un directorio: source (. ml) e interface (.mli) los archivos se almacenan juntos. Puede ser que si el proyecto es demasiado grande, vale la pena introducir más subdirectorios.
  • El directorio _build está bajo la influencia de OASIS build system. Almacena archivos de origen y objeto allí y me gusta que los archivos de compilación no se interfieran con los archivos de origen, por lo que puedo eliminarlo fácilmente en caso de que algo ocurra Equivocada.
  • Almaceno varios scripts de shell en el directorio scripts. Algunos de ellos son para la ejecución de pruebas y la generación de archivos de interfaz.
  • Todos los archivos de entrada y salida para las pruebas los almaceno en un directorio separado.

Interfaces / Documentación

El uso de archivos de interfaz (.mli) tiene ventajas e inconvenientes para mí. Realmente ayuda encontrar errores de tipo, pero si los tienes, también tienes que editarlos al hacer cambios o mejoras en tu código. A veces olvidar esto causa errores desagradables.

Pero la razón principal por la que me gustan los archivos de interfaz es la documentación. Utilizo ocamldoc para generar (OASIS soporta esta característica con la bandera -doc) páginas html con documentación automáticamente. En mi opinión, es suficiente escribir comentarios que describan cada función en la interfaz y no insertar comentarios en el medio del código. En OCaml las funciones suelen ser cortas y concisas y si hay necesidad de insertar extra comentarios allí, puede ser que es mejor dividir la función.

También tenga en cuenta la bandera -i para ocamlc. El compilador puede generar automáticamente un archivo de interfaz para un módulo.

Pruebas

No encontré una solución razonable para soportar pruebas (me gustaría tener alguna aplicación ocamltest), por eso estoy usando mis propios scripts para ejecutar y verificar casos de uso. Afortunadamente, OASIS admite la ejecución de comandos personalizados cuando setup.ml se ejecuta con -test bandera.

No uso OASIS durante mucho tiempo y si alguien conoce otras características interesantes, también me gustaría saber sobre ellas.

Además, si no eres consciente de OPAM, definitivamente vale la pena mirar. Sin él, instalar y administrar nuevos paquetes es una pesadilla.

 5
Author: Pavel Zaichenkov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-14 00:45:57