programming language caracteristicas design functional-programming ocaml

design - language - ocaml caracteristicas



diseƱando grandes proyectos en OCaml (3)

OASIS

Para agregar 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 objetivo de ''lanzamiento'' 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 que crea automáticamente tarball para cargar.

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 xUnit testing.

Control / gestión de la fuente

Descargo de responsabilidad: soy el propietario / mantenedor de forge.ocamlcore.org (también conocido como forge.oo)

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 forge.oo

En ambos casos, es obligatorio tener una lista de correo pública a la que envíe todas las notificaciones de compromiso, para que todos puedan verlas y revisarlas. Puede usar grupos de Google o una lista de correo en forge.oo

Recomiendo tener una bonita página web (github o forge.oo) con la documentación de OCamldoc cada vez que se comprometa. Si tiene una gran base de código, esto le ayudará a utilizar la documentación generada por OCamldoc desde el principio (y solucionarlo rápidamente).

Recomiendo crear tarballs cuando alcances una etapa estable. No confíe solo en consultar 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 comprimidos en un lugar central (un repositorio git es una buena idea para eso).

¿Cuál es la mejor práctica para escribir grandes proyectos de software en OCaml?

¿Cómo estructuras tus proyectos?

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

¿Sistema de compilación? ¿Marco de prueba? Pila de la biblioteca?

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


Probablemente este no responda tu pregunta por completo, pero esta es mi experiencia con respecto al entorno de construcción:

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

Sistema de compilación

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

Estructura

Por lo general, mi proyecto que está 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: los archivos fuente (.ml) e interfaz (.mli) 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 del sistema de compilación OASIS. Almacena los archivos fuente y objeto allí y me gusta que los archivos compilados no se vean interferidos con los archivos fuente, por lo que puedo eliminarlos fácilmente en caso de que algo salga mal.
  • Almacenar múltiples scripts de shell en el directorio de scripts . Algunos de ellos son para ejecución de prueba y generación de archivos de interfaz.
  • Todos los archivos de entrada y salida para las pruebas que almaceno en un directorio separado.

Interfaces / Documentación

El uso de archivos de interfaz (.mli) tiene ventajas y desventajas para mí. Realmente ayuda encontrar errores de tipo, pero si los tiene, debe editarlos también cuando realice cambios o mejoras en su 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. Uso ocamldoc para generar (OASIS admite esta característica con -doc flag) páginas html con documentación automáticamente. En mi opinión, es suficiente escribir comentarios describiendo 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 es necesario insertar comentarios adicionales allí, es mejor dividir la función.

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

Pruebas

No encontré una solución razonable para las pruebas de soporte (me gustaría tener alguna aplicación ocamltest ), es por eso que 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 el indicador de -test .

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

Además, si no conoces OPAM , definitivamente vale la pena mirarlo. Sin eso, instalar y administrar paquetes nuevos es una pesadilla.


Voy a responder para 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 de código:

  • un script de shell con capacidad de fuente define PATH y otras variables para nuestro proyecto
  • un archivo .ocamlinit en la raíz de nuestro proyecto carga un grupo de bibliotecas al iniciar una sesión de nivel superior
  • omake, que es rápido (con la opción -j para construcciones paralelas); pero evitamos hacer locos complementos omake personalizados
  • un archivo Make de raíz contiene todos los objetivos esenciales (configuración, compilación, prueba, limpieza y más)
  • un nivel de subdirectorios, no dos
  • la mayoría de los subdirectorios compilan en una biblioteca OCaml
  • algunos subdirectorios contienen otras cosas (configuración, scripts, etc.)
  • OCAMLPATH contiene la raíz del proyecto; cada subdirectorio de biblioteca produce un archivo META, haciendo que todas las partes de OCaml de los proyectos sean accesibles desde el nivel superior utilizando #require.
  • solo se ha creado un ejecutable de OCaml para todo el proyecto (ahorra mucho tiempo de vinculación, aún no estoy seguro de por qué)
  • las bibliotecas se instalan mediante una secuencia de comandos de instalación utilizando opam
  • los paquetes opam locales se hacen para el software que no está en el repositorio oficial de opam
  • 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 de código fuente:

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

Control y gestión de la fuente:

  • usamos git y github
  • todo el código nuevo es revisado por pares a través de solicitudes de extracción de github
  • Los archivos tar para bibliotecas que no son opam y no github se almacenan en un repositorio git separado (que puede volarse si la historia se hace demasiado grande)
  • Las bibliotecas de última generación existentes en github se bifurcan en nuestra cuenta github y se instalan a través de nuestro propio paquete opam local

Uso de OCaml:

  • OCaml no compensará las malas prácticas de programación; enseñar 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 las etiquetas de campo de registro y los constructores de variante (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 obligatorio por alguna biblioteca externa
  • en teoría, desde OCaml 4.01.0 deberíamos preferir las variantes clásicas a las variantes polimórficas
  • utilizamos excepciones para indicar los errores y los dejamos pasar felices hasta que nuestro bucle de servidor principal los capte y los interprete como "error interno" (predeterminado), "solicitud incorrecta" o alguna otra cosa
  • excepciones como Exit o Not_found se pueden usar localmente cuando tiene sentido, pero en las interfaces de los módulos preferimos usar opciones.

Bibliotecas, protocolos, marcos:

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

"Servicios en la nube:

  • utilizamos bastantes de ellos, ya que generalmente son baratos, fáciles de interactuar y resuelven problemas de escalabilidad y mantenimiento para nosotros.

Pruebas:

  • tenemos un objetivo make / omake para pruebas rápidas y uno para pruebas lentas
  • las pruebas rápidas son pruebas unitarias; cada módulo puede proporcionar una función de "prueba"; un archivo test.ml ejecuta la lista de pruebas
  • las pruebas lentas son aquellas que implican la ejecución de servicios múltiples; 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, a excepción de los servicios en la nube para los que encontramos formas 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 existe un marco que se encargue de todo eso, pero al menos se puede elegir las herramientas.