Redundancia en la declaración de tipo OCaml (ml / mli)


Estoy tratando de entender una cosa específica sobre los módulos ocaml y su compilación:

¿Estoy obligado a volver a declarar los tipos ya declarados en un .mli dentro de las implementaciones .ml específicas?

Solo para dar un ejemplo:

(* foo.mli *)
type foobar = Bool of bool | Float of float | Int of int

(* foo.ml *)
type baz = foobar option

Esto, de acuerdo con mi forma normal de pensar sobre interfaces/implementaciones, debería estar bien, pero dice

Error: Unbound type constructor foobar

Al intentar compilar con

ocamlc -c foo.mli
ocamlc -c foo.ml

Por supuesto el error desaparece si declaro foobar dentro de foo.ml también, pero parece una forma compleja ya que tengo que mantener las cosas sincronizadas en cada cambio.

¿Hay alguna manera de evitar esta redundancia o me veo obligado a volver a declarar tipos cada vez?

Gracias de antemano

Author: Jack, 2010-07-13

5 answers

OCaml intenta forzarlo a separar la interfaz (.mli) de la implementación (.ml. La mayoría de las veces, esto es algo bueno; para los valores, se publica el tipo en la interfaz y se mantiene el código en la implementación. Se podría decir que OCaml está imponiendo una cierta cantidad de abstracción (las interfaces deben publicarse; no hay código en las interfaces).

Para los tipos, muy a menudo, la implementación es la misma que la interfaz: ambos declaran que el tipo tiene una representación particular (y tal vez que la declaración de tipo es generativa). Aquí, no puede haber abstracción, porque el implementador no tiene ninguna información sobre el tipo que no quiere publicar. (La excepción es básicamente cuando se declara un tipo abstracto.)

Una forma de verlo es que la interfaz ya contiene suficiente información para escribir la implementación. Dada la interfaz type foobar = Bool of bool | Float of float | Int of int, solo hay una implementación posible. ¡Así que no escribas una implementación!

A el modismo común es tener un módulo dedicado a declaraciones de tipo, y hacer que tenga solo un .mli. Dado que los tipos no dependen de los valores, este módulo normalmente aparece muy temprano en la cadena de dependencias. La mayoría de las herramientas de compilación funcionan bien con esto; por ejemplo ocamldep hará lo correcto. (Esta es una ventaja sobre tener solo un .ml.)

La limitación de este enfoque es cuando también necesita algunas definiciones de módulo aquí y allá. (Un ejemplo típico es definir un tipo foo, luego un módulo OrderedFoo : Map.OrderedType con type t = foo, luego una declaración de tipo adicional que involucre'a Map.Make(OrderedFoo).t.) Estos no se pueden poner en archivos de interfaz. A veces es aceptable dividir sus definiciones en varios trozos, primero un montón de tipos (types1.mli), luego un módulo (mod1.mli y mod1.ml), luego más tipos (types2.mli). Otras veces (por ejemplo, si las definiciones son recursivas) tienes que vivir con un .ml sin un .mli o duplicación.

 16
Author: Gilles,
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-12-19 02:06:59

Sí, se ve obligado a volver a declarar los tipos. Las únicas formas de evitarlo que conozco son

  • No uses una .archivo mli; simplemente exponga todo sin interfaz. Terrible idea.

  • Utilice una herramienta de programación alfabética u otro preprocesador para evitar duplicar las declaraciones de interfaz en la Única Fuente Verdadera. Para los grandes proyectos, lo hacemos en mi grupo.

Para proyectos pequeños, solo duplicamos declaraciones de tipo. Y refunfuñar sobre se.

 14
Author: Norman Ramsey,
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
2010-07-13 15:27:24

Puede dejar que ocamlc genere el archivo mli por usted desde el archivo ml:

ocamlc -i some.ml > some.mli
 12
Author: aneccodeal,
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
2010-07-13 20:18:39

En general, sí, se requiere duplicar los tipos.

Sin embargo, puede solucionar esto con Camlp4 y la extensión de sintaxis pa_macro (findlib package: camlp4.macro). Define, entre otras cosas, e INCLUYE construct. Puede usarlo para factorizar las definiciones de tipos comunes en un archivo separado e incluir ese archivo en los archivos .ml y .mli. Sin embargo, no he visto que esto se haga en un proyecto OCaml desplegado, por lo que no se si calificaría como práctica recomendada, pero es posible.

La solución de programación alfabetizada, sin embargo, es IMO más limpia.

 3
Author: Michael Ekstrand,
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
2010-07-14 02:06:14

No, en el archivo mli, simplemente diga "type foobar". Esto funcionará.

 -3
Author: dsgbdg,
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
2010-11-19 22:16:40