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
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ą.
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.
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
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.
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.
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