Krytyka OCaml: czy nadal jest aktualna?


15

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


5
Głosuję za zamknięciem tego pytania jako nie na temat, ponieważ meta.programmers.stackexchange.com/questions/6417/…
— Philipp

3
Jest widelec gdzie te mogłyby już naprawiono: tryfsharp.org
— Den

7
Oczywiście opinia w tym eseju, który podłączyłeś, jest słuszna, ale to, czy te krytyki są dla ciebie istotne, jest zupełnie inną sprawą. Zauważ, że autor pochodzi ze wspólnego Lisp i dlatego ma wartości Lispish. Jeśli przyjmiesz inne podejście do OCaml niż porównanie go z Lisp (np. „OCaml to Haskell dla zwykłych śmiertelników” lub „Gdyby C był językiem funkcjonalnym, miałbyś OCaml”), okaże się, że jest to o wiele bardziej satysfakcjonujące. Pomimo wszystkich swoich wad OCaml jest świetnym językiem i mimo wszystko zachęcam do zagłębienia się w nim.
— amon

5
O ile mi wiadomo, nie ma sposobu na wykrycie przepełnienia liczb całkowitych w sprzęcie , więc aby je wykryć , trzeba będzie wprowadzić kontrole środowiska wykonawczego; ale autor odrzucił użycie „bignum” na podstawie wydajności! Skarga dotycząca pisania statycznego sprowadza się do stwierdzenia, że ​​pasy bezpieczeństwa są złe, ponieważ możesz myśleć, że nie możesz umrzeć w wypadku samochodowym. Skarga na niezmienność modułu mówi, że chce on małpować rzeczy - praktykę antymodularną i podatną na błędy. „Małe zoo typu” nie ma nic wspólnego z wnioskowaniem typu. Oczywiste jest, gdzie leżą jego uprzedzenia .
— Doval

2
@Doval: Oczywiście można wykryć przepełnienie sprzętu. Jednak radzenie sobie z tym jest kwestią oprogramowania.
— Mason Wheeler,

Odpowiedzi:


13

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

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

    2. „ 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:

     let f x y = x + y ;;
     let g a b = f b a ;;
     let f x y = x * y ;;
     exit (g 2 3) ;;
    

    Powyższy program kompiluje, a po uruchomieniu zwraca 5, ponieważ gjest zdefiniowany w pierwszej wersji f, tak jak gdyby funkcja wywołująca gwstawiała wywołanie do f. 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, ...).

Słabość językowa

  • Rekordowe nazewnictwo piekła

    To prawda, ale powinieneś używać modułów:

    1. Dwa pola dwóch rekordów mają tę samą etykietę w OCaml
    2. Rozwiązywanie nazw pól
  • 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 map2dla tablic.

  • Brak zmiennych dynamicznych

    Można wdrożyć:

    1. http://okmij.org/ftp/ML/dynvar.txt
    2. http://okmij.org/ftp/ML/index.html#dynvar
  • 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,

„Powinieneś wypróbować OCaml (lub jeszcze lepiej Haskell), nawet jeśli uważasz, że jest do bani i nie planujesz go używać. Bez tego twoje wykształcenie informatyczne jest niekompletne, podobnie jak niekompletne bez niektórych Lisp i C (lub , jeszcze lepiej, Zgromadzenie) ”.

... nadal obowiązuje.


Dziękuję, rzuciłem okiem na linki, ale tak naprawdę nie pytam, czy te opinie są uzasadnione. Pytam, czy fakty nadal istnieją. Na przykład: czy istnieje sposób na wydrukowanie dowolnego typu danych algebraicznych? Czy nadal jest dokładne, że liczby całkowite przepełniają się bez ostrzeżeń? Czy istnieje dziś sposób działania na plikach i usuwania ich bez konieczności pisania za każdym razem, gdy płyta główna zajmuje się zamykaniem plików w przypadku błędów?
— Andrea

@Andrea Zredagowałem swoją odpowiedź.
— rdzeń rdzeniowy

1
„... są ludzie, którzy wolą to zamiast wieży numerycznej Lispa (nie wiem dlaczego). Przypuszczam, że to pomaga w wnioskowaniu typu”. Bingo System typów SML i OCaml wymaga, aby każde wyrażenie miało tylko jeden typ. Przeciążenie operatorów matematycznych przez SML jest wyjątkiem wbudowanym w język. Dotyczy to również Haskell; umożliwienie tego rodzaju przeciążenia było motywacją do stworzenia klas typów. Chodzi o to, że dla każdego typu można mieć tylko jedną instancję klasy typu. Nie można również ślepo przekonwertować liczby całkowitej na liczbę zmiennoprzecinkową o równej wielkości - liczba zmiennoprzecinkowa 64-bitowa ma tylko 54 bity dokładności.
— Doval

@Doval Co powiesz na coś takiego jak wpisanie wieży numerycznej do rakiety? Czy możemy sobie wyobrazić bibliotekę OCaml, która wykorzystywałaby klasy typów lub uogólnione algebraiczne typy danych (GADT) w celu zapewnienia polimorficznych operatorów matematycznych? Odnośnie konwersji: nie wszystkie operacje są możliwe, ale niektóre są i można je wpisać.
— rdzeń rdzeniowy

2
@Doval: „Re: polimorficzne operatory matematyczne, nie sądzę, że jest jakikolwiek sposób bez implementacji klas typów Haskell”. F # ma polimorficzne operatory matematyczne bez klas typów.
— Jon Harrop,

7

Widząc, że strona jest dość stara (2007): który z wymienionych tam punktów jest nadal aktualny?

  • 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, includejeś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 ... finallykonstrukcji 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 consfunkcji. 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 typedefinicje są domyślnie rekurencyjne, podczas gdy letwiązania nie są domyślnie rekurencyjne.

  • Runda funkcyjna jest nieobecna . OCaml wciąż ma standardową zawartość, ale biblioteki stron trzecich, takie jak Core Jane St roundi przyjaciele, zapewniają .

  • Listy . List.mapwciąż 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.

Na przykład: czy nadal jest prawdą, że nie można wydrukować ogólnego obiektu?

Kiedy napisał, że nie można po prostu zrobić:

print value

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.


Dopasowanie wzorca: kod źródłowy OCaml i optymima odnoszą się do tego samego papieru: „Optymalizacja dopasowania wzorca”. Powiedziałbym, że nie można tutaj realistycznie zastosować ani „ad-hoc”, „pomyłki” ani „nieformalnie określonego”. „F # ma świetne rozwiązanie tego problemu”: Naprawdę chciałbym zobaczyć trochę więcej szczegółów na ten temat, jeśli to możliwe. Odpowiedź jest dobra, ale przeklinanie, gdy mówi się o niej, consnadaje zły ton (oryginalny artykuł jest rantem, ale nie trzeba go z niego kopiować).
— coredump

2
@coredump: „Naprawdę chciałbym zobaczyć trochę więcej szczegółów na ten temat, jeśli to możliwe”. F # ma zdefiniowany przez użytkownika polimorfizm ad-hoc na niektórych operatorach i funkcjach. Możesz więc używać +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.
— Jon Harrop,

@coredump: „Kod źródłowy OCaml i optymima odnoszą się do tego samego papieru”. Techniki opisane w tym artykule są w całości oparte na systemie typu ML. System typów, którego Lisp nie ma. Optima nie robi i nie może wiele z tego, co opisano w tym artykule. Wystarczy spojrzeć na punkt 4.2 „Korzystanie z informacji o wyczerpaniu” W Lisp nie ma informacji o kompletności, ponieważ nie ma typów wariantów. Na przykład OCaml wybiera między zagnieżdżonymi skokami lub tabelą wysyłki w oparciu o liczbę liści, ale ta informacja nie jest znana w Lisp.
— Jon Harrop,

Lisp i OCaml są zaprojektowane do różnych rzeczy (np. Dynamiki, programowania obrazowego). Ale Lisp ma system typu, różny od Hindley-Milner i implementacje nie wykorzystać go podczas kompilacji. Spójrz na tę sesję SBCL z przykładami z sekcji 4.2 artykułu. Kompilator jest już sprawdzany przez kompilator na podstawie wnioskowania o typie i deklaracji. Optima może dodać kod specyficzny dla implementacji dla makropolecenia w czasie kompilacji (lub VOP SBCL), aby mieć inne strategie, ale nie ma wystarczających zachęt, aby to zrobić.
— rdzeń rdzeniowy

Jak to się ma do zdefiniowanego przez użytkownika algebraicznego typu danych?
— Jon Harrop
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.