Redundancja w deklaracji typu OCaml (ml/mli)

Próbuję zrozumieć konkretną rzecz dotyczącą modułów ocaml i ich kompilacji:

Czy jestem zmuszony do redeclare typów już zadeklarowanych w .mli wewnątrz konkretnych .ml implementacji?

Dla przykładu:

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

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

To, zgodnie z moim normalnym sposobem myślenia o interfejsach/implementacjach, powinno być ok, ale mówi

Error: Unbound type constructor foobar

Podczas próby kompilacji z

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

Oczywiście błąd zniknie, jeśli zadeklaruję foobar wewnątrz foo.ml zbyt, ale wydaje się to skomplikowany sposób, ponieważ muszę zachować rzeczy zsynchronizowane na każdej zmianie.

Czy jest sposób, aby uniknąć tej redundancji lub jestem zmuszony do ponownego deklarowania typów za każdym razem?

Z góry dzięki

Author: Jack, 2010-07-13

5 answers

OCaml próbuje zmusić cię do oddzielenia interfejsu (.mli) od implementacji (.ml. W większości przypadków jest to dobra rzecz; dla wartości publikujesz typ w interfejsie i zachowujesz kod w implementacji. Można by powiedzieć, że OCaml wymusza pewną ilość abstrakcji (interfejsy muszą być publikowane; żadnego kodu w interfejsach).

Dla typów, bardzo często, implementacja jest taka sama jak interfejs: oba stwierdzają, że typ ma określoną reprezentację (i być może, że deklaracja typu jest generatywna). Tutaj nie może być abstrakcji, ponieważ wykonawca nie ma żadnych informacji o typie, którego nie chce opublikować. (Wyjątek jest w zasadzie wtedy, gdy deklarujesz Typ abstrakcyjny.)

Jednym ze sposobów na to jest to, że interfejs zawiera już wystarczająco dużo informacji, aby napisać implementację. Biorąc pod uwagę Interfejs type foobar = Bool of bool | Float of float | Int of int, możliwa jest tylko jedna implementacja. Więc nie pisz realizacji!

A wspólny idiom to mieć moduł dedykowany do deklaracji typu i sprawić, że będzie miał tylko .mli. Ponieważ typy nie zależą od wartości, moduł ten zazwyczaj pojawia się bardzo wcześnie w łańcuchu zależności. Większość narzędzi kompilacyjnych dobrze sobie z tym radzi; na przykład ocamldep zrobi to, co należy. (Jest to jedna przewaga nad posiadaniem tylko .ml.)

Ograniczenie tego podejścia jest wtedy, gdy potrzebujesz również kilku definicji modułów tu i tam. (Typowym przykładem jest definiowanie typu foo, następnie moduł OrderedFoo : Map.OrderedType z type t = foo, następnie kolejna deklaracja typu zawierająca 'a Map.Make(OrderedFoo).t.) Nie można ich umieścić w plikach interfejsu. Czasami dopuszczalne jest podzielenie definicji na kilka części, najpierw kilka typów (types1.mli), potem moduł (mod1.mli i mod1.ml), a następnie więcej typów (types2.mli). Innym razem (na przykład, jeśli definicje są rekurencyjne) musisz żyć z .ml Bez .mli lub duplikacją.

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-19 02:06:59

Tak, jesteś zmuszony do redeclare typów . Jedyne sposoby na obejście tego, jakie znam to

  • Nie używaj .plik mli; wystarczy odsłonić wszystko bez interfejsu. Okropny pomysł.

  • Użyj narzędzia programistycznego lub innego preprocesora, aby uniknąć powielania deklaracji interfejsu w jednym prawdziwym źródle. W przypadku dużych projektów robimy to w mojej grupie.

W przypadku małych projektów po prostu powielamy deklaracje typu. I marudzić o to.

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-13 15:27:24

Możesz pozwolić ocamlc wygenerować plik mli dla Ciebie z pliku 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/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-13 20:18:39

Ogólnie rzecz biorąc, tak, jesteś zobowiązany do powielania typów.

Możesz obejść ten problem, używając Camlp4 i rozszerzenia składni pa_macro (pakiet findlib: camlp4.macro). Definiuje między innymi i obejmuje konstruowanie. Można go użyć do uwzględnienia wspólnych definicji typu w osobnym pliku i dołączyć ten plik zarówno do .ml, jak i .mli. Nie widziałem tego jednak w wdrożonym projekcie OCaml, więc nie wiem, czy kwalifikowałoby się to jako Zalecana Praktyka, ale to możliwe.

Rozwiązanie programistyczne jest jednak czystsze IMO.

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-14 02:06:14

Nie, w pliku mli wystarczy powiedzieć "type foobar". To zadziała.

 -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/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-19 22:16:40