Jestem kompletnym nowicjuszem z OCaml. Niedawno natknąłem się na tę stronę, wymieniając sporo krytyki wobec OCaml.
Widząc, że strona jest dość stara (2007): który z wymienionych tam punktów jest nadal aktualny? Na przykład: czy nadal jest prawdą, że nie można wydrukować ogólnego obiektu?
Chcę wyjaśnić, że nie szukam dyskusji na temat wyrażonych w nich opinii. Pytam, czy wymienione informacje, takie jak fakt, że liczby całkowite są przepełnione bez ostrzeżeń, nadal są poprawne dla nowszych wersji OCaml
Odpowiedzi:
Ten artykuł jest omawiany w kilku miejscach:
Podsumowując: tak, OCaml nie jest Lispem i nie, nie jest doskonały (co to znaczy?). Nie sądzę, aby punkty wymienione w poście na blogu były przydatne dla codziennych programistów O'Caml.
Po przestudiowaniu O'Caml uważam, że jest to interesujący język, który może pomóc w tworzeniu programów, o których nawet nie odważyłbyś się pisać, powiedzmy w C / C ++ / Java: na przykład, spójrz na Frama-C .
Aby uzyskać aktualny opis O'Caml, zachęcam do zapoznania się z jego funkcjami : język promuje silne techniki sprawdzania typu statycznego, które pozwalają implementacjom skupić się na tworzeniu wydajnych, ale bezpiecznych środowisk uruchomieniowych.
Ważne : nie jestem ekspertem OCaml: jeśli jesteś jednym z nich i widzisz, że napisałem coś strasznie złego, popraw mnie. Zmienię odpowiednio ten post.
Sprawdzanie typu statycznego
Fałszywe poczucie bezpieczeństwa
To prawda, ale oczywiste.
Wpisywanie statyczne daje dowody, którym możesz zaufać w odniesieniu do podzbioru właściwości programu. O ile nie zaakceptujesz formalnego postępowania, przeciętny program (niebędący zabawkami) będzie podlegał błędom programistycznym, które można zaobserwować tylko w czasie wykonywania.
Wtedy można zastosować techniki sprawdzania dynamicznego: kompilator OCaml ma flagi do generowania plików wykonywalnych z informacjami debugowania i tak dalej ... Lub może generować kod, który ślepo ufa programatorowi i usuwa informacje o typie w jak największym stopniu. Programiści, którzy chcą solidnych programów, powinni wyraźnie wdrożyć kontrole dynamiczne.
To samo dotyczy np. Common Lisp, ale odwrócone: najpierw typy dynamiczne, z opcjonalnymi deklaracjami typów i dyrektywami kompilatora po drugie.
Kilka podstawowych typów
Nadal obowiązuje: podstawowy język nie zmienił się (lub nie dramatycznie).
Ciche przepełnienie liczb całkowitych
Jest to norma w większości języków, że przepełnienie liczb całkowitych jest sprawdzane ręcznie. Nie znam żadnej biblioteki, która sprawdziłaby operacje typu, aby sprawdzić, czy może dojść do przepełnienia.
Niezmienność modułu
Autor wspomina Functors, ale nie widzę, jak jego przykład nie może zostać zaimplementowany. Czytając rozdział dotyczący modułów pierwszej klasy https://realworldocaml.org , wydaje się, że modułów można używać do komponowania i budowania nowych modułów. Oczywiście modyfikacja istniejącego modułu wymaga modyfikacji kodu źródłowego, ale znowu nie jest to rzadkie wśród języków programowania.
„ Semantycznie funkcje są kompilowane INLINE”
Wątek reddit powyżej nie zgadza się, mówiąc, że wiązanie jest rozwiązywane w czasie łącza. Jest to jednak szczegół implementacji i myślę, że podkreślony Semantycznie odnosi się do sposobu rozwiązywania funkcji. Przykład:
Powyższy program kompiluje, a po uruchomieniu zwraca 5, ponieważ
g
jest zdefiniowany w pierwszej wersjif
, tak jak gdyby funkcja wywołującag
wstawiała wywołanie dof
. Nawiasem mówiąc, nie jest to „złe”, jest po prostu zgodne z zasadami cieniowania nazw O'Caml.Podsumowując : tak, moduły są niezmienne . Ale można je również komponować .
Polimorfizm powoduje błędy typu Run-time
Nie mogę odtworzyć wspomnianego błędu. Podejrzewam, że to błąd kompilatora.
Bez makr
Rzeczywiście, nie ma makr, ale preprocesory (OcamlP4, OcamlP5, ...).
Opakowania (z otwartym plikiem)
Wiedząc, jak UNWIND-PROTECT może być przydatny, naprawdę bolesne jest to, że nie ma go w większej liczbie języków. Oto kilka opcji:
Miejsca
Nie sądzę, że w OCaml istnieją ogólne odniesienia.
Słabość językowa
Rekordowe nazewnictwo piekła
To prawda, ale powinieneś używać modułów:
Składnia
Nadal obowiązuje (ale tak naprawdę to tylko składnia).
Bez polimorfizmu
Nadal obowiązuje, ale w jakiś sposób są ludzie, którzy wolą to zamiast wieży numerycznej Lispa (nie wiem dlaczego). Przypuszczam, że to pomaga w wnioskowaniu typu.
Niespójne zestawy funkcji
Zobacz projekt Dołączone baterie OCaml . W szczególności BatArray , na przykład
map2
dla tablic.Brak zmiennych dynamicznych
Można wdrożyć:
Opcjonalne ~ argumenty do bani
Ze względu na ograniczenia językowe nie można mieszać argumentów opcjonalnych i słów kluczowych we wspólnym Lisp. Czy to znaczy, że jest do bani? (oczywiście można to zmienić za pomocą makr (patrz np. moja odpowiedź )). Zobacz dokumentację O'Caml dla opcjonalnych i nazwanych argumentów w O'Caml.
Częściowa niespójność aplikacji argumentów
Nie wydaje mi się, żeby to było naprawdę denerwujące w praktyce.
Czytelność arytmetyki
Może się przydać, ale jeśli wolisz, możesz użyć R lub Pythona do rozwiązywania problemów numerycznych.
Rozwiązywanie konfliktów nazw niemych
Nadal obowiązuje, ale pamiętaj, że jest to dobrze udokumentowane.
Brak wejścia / wyjścia obiektu
Nadal obowiązuje.
Implementacja, biblioteki
Te zmieniają się każdego dnia: nie ma ostatecznej odpowiedzi.
Wreszcie,
... nadal obowiązuje.
źródło
Fałszywe poczucie bezpieczeństwa . To nonsens.
Kilka podstawowych typów . OCaml ma teraz bajty i tablice bajtów, ale nie ma wbudowanych ciągów Unicode, 16-bitowych liczb całkowitych, liczb całkowitych bez znaku, 32-bitowych liczb zmiennoprzecinkowych, wektorów lub macierzy. Biblioteki innych firm zapewniają niektóre z nich.
Ciche przepełnienie liczb całkowitych . Bez zmian, ale nigdy nie było problemu.
Niezmienność modułu . Jego zalecenie, aby funkcje i moduły były modyfikowalne, jest ponurą odpowiedzią na Lisp i naprawdę złym pomysłem. Możesz zastąpić moduły,
include
jeśli chcesz, ale oczywiście nie możesz ich mutować.Polimorfizm powoduje błędy typu Run-time . Jest to duży problem z OCaml i nie został naprawiony. Gdy twoje typy ewoluują pod względem równości polimorficznej, porównywanie i mieszanie zaczną się nie powieść, gdy napotkają takie typy jak funkcje, a debugowanie problemu jest bardzo trudne. F # ma świetne rozwiązanie tego problemu.
Bez makr . Jak na ironię, kiedy to napisał, OCaml faktycznie miał pełne wsparcie dla makr, ale teraz postanowili wycofać tę funkcję.
Owijarki . To był prawdziwy problem i nie został naprawiony. Nadal nie ma
try ... finally
konstrukcji w języku OCaml i żadne opakowanie nie implementuje jej w stdlib.Miejsca . Bez zmian, ale bez problemu.
Rekordowe nazewnictwo piekła . Zbuduj swój kod poprawnie za pomocą modułów.
Składnia . Bez zmian, ale bez problemu.
Bez polimorfizmu . Było to głównie nonsensem, kiedy to napisał i nic się nie zmieniło.
Niespójne zestawy funkcji . OCaml nadal nie ma
cons
funkcji. W porządku. Nie chcę rzeczy Lisp w moim języku, dziękuję.Brak zmiennych dynamicznych . To było dobre w OCaml. To wciąż dobra rzecz w OCaml.
Opcjonalne ~ argumenty do bani . Opcjonalne argumenty rock. Odznaczyłem Microsoft, aby kazał im dodać opcjonalne argumenty do F #.
Częściowa niespójność aplikacji argumentów . Co?
Czytelność arytmetyki . Zmieniło się to odkąd przestałem używać OCaml ~ 8 lat temu. Najwyraźniej teraz możesz to zrobić
Int64.((q * n - s * s) / (n - 1L))
.Rozwiązywanie konfliktów nazw niemych . Próbował w pełni rozwinąć oprogramowanie w REPL, tak jak w Lisp. Nie rób tego w OCaml. Używaj plików i kompilacji wsadowej odwołując się do REPL tylko do testowania, uruchamiania kodu jednorazowego i interaktywnych obliczeń technicznych.
Kolejność oceny . To było złe, kiedy to napisał. Kolejność oceny jest niezdefiniowana w OCaml.
Brak wejścia / wyjścia obiektu . Przytoczył bibliotekę strony trzeciej, która już rozwiązała ten „problem”.
Kompilator zatrzymuje się po pierwszym błędzie . Co?
Brak śladu stosu dla natywnie skompilowanych plików wykonywalnych . Naprawiony.
Debuger jest do bani . Nigdy nie korzystałem z debuggera. Statyczne sprawdzanie typu wyłapuje prawie wszystkie moje błędy.
GC jest do bani . Odkryłem, że GC OCaml jest znakomity, z wyjątkiem jednego poważnego problemu: globalna blokada uniemożliwia programowanie równoległe.
Brak dorozumianych deklaracji forward . Wzajemna rekurencja jest jawna z założenia we wszystkich ML. Jedynym dziwactwem jest to, że
type
definicje są domyślnie rekurencyjne, podczas gdylet
wiązania nie są domyślnie rekurencyjne.Runda funkcyjna jest nieobecna . OCaml wciąż ma standardową zawartość, ale biblioteki stron trzecich, takie jak Core Jane St
round
i przyjaciele, zapewniają .Listy .
List.map
wciąż nie jest rekurencyjny. Przesłałem łatki, aby naprawić poważne błędy tego typu i musiałem czekać lata, zanim pojawią się w wydaniach. Oczywiście listy są niezmienne. I tak powinny być.Prędkość . Wierzę, że czasy kompilacji dużych wariantów polimorficznych zostały naprawione.
Dopasowywanie wzorów . Triumf nadziei nad rzeczywistością. Społeczność Lisp tego nie zrobiła. Stąd moja 10 zasada: każdy wystarczająco skomplikowany program Lisp zawiera ad-hoc, nieformalnie określoną i pozbawioną błędów implementację połowy kompilatora dopasowywania wzorców OCaml.
Kiedy napisał, że nie można po prostu zrobić:
ale możesz wywołać ładną drukarkę z najwyższego poziomu jako wywołanie biblioteki, podając jej niezbędne informacje o typie. Było też makro, którego można użyć do opisywania struktur danych w celu automatycznego generowania ładnych drukarek.
źródło
cons
nadaje zły ton (oryginalny artykuł jest rantem, ale nie trzeba go z niego kopiować).+
na liczbach całkowitych, zmiennoprzecinkowych i kompleksach, ale możesz także definiować własne typy i dodawać przeciążenia+
do pracy na swoim typie. Zapewnia to zwięzłość i czytelność Lisp lub Haskell z przewidywalnie dobrą wydajnością SML lub OCaml, osiągając coś, czego nie robi żaden inny język.