Critiche OCaml: è ancora valido?


15

Sono un principiante completo con OCaml. Di recente mi sono imbattuto in questa pagina in cui sono elencate molte critiche nei confronti di OCaml.

Visto che la pagina è piuttosto vecchia (2007): quali dei punti elenco elencati sono ancora veri oggi? Ad esempio: è ancora vero che è impossibile stampare un oggetto generico?

Voglio chiarire che non sto cercando una discussione delle opinioni ivi espresse. Sto chiedendo se le informazioni elencate, come il fatto che gli overflow di numeri interi senza avvisi, siano ancora corretti per le versioni più recenti di OCaml


5
Sto votando per chiudere questa domanda come fuori tema perché meta.programmers.stackexchange.com/questions/6417/…
Philipp

3
C'è un fork in cui potrebbero essere stati risolti: tryfsharp.org
Den,

7
Ovviamente l'opinione in quel saggio che hai collegato è valida, ma se queste critiche sono rilevanti per te è una questione completamente diversa. Si noti che l'autore proviene da un background Lisp comune e quindi ha valori Lispish. Se adotti un altro approccio a OCaml rispetto al paragonarlo a Lisp (ad esempio "OCaml è Haskell per semplici mortali" o "Se C fosse un linguaggio funzionale, avresti OCaml") lo troverai molto più soddisfacente. Nonostante tutti i suoi difetti, OCaml è un ottimo linguaggio e ti incoraggio comunque a dilettarti.
amon,

5
Per quanto ne so non c'è modo di rilevare l'overflow di numeri interi nell'hardware , quindi è necessario inserire controlli di runtime ovunque per rilevarlo; ma l'autore ha respinto usando "bignum" sulla base della performance! La lamentela sulla battitura statica equivale a dire che le cinture di sicurezza sono cattive perché potresti pensare di non poter morire in un incidente d'auto. La lamentela sull'immutabilità del modulo sta dicendo che vuole mettere insieme le cose: una pratica anti-modulare e soggetta a errori. Il "piccolo zoo di tipo" non ha nulla a che fare con l'inferenza del tipo. È chiaro dove si trovano i suoi pregiudizi .
Doval,

2
@Doval: ovviamente puoi rilevare l'overflow nell'hardware. Trattare con esso, tuttavia, è una questione di software.
Mason Wheeler,

Risposte:


13

Questo articolo è discusso in diversi luoghi:

Riassumendo: sì, OCaml non è un Lisp, e no, non è perfetto (cosa significa?). Non credo che i punti menzionati nel post sul blog siano rilevanti per i programmatori O'Caml di tutti i giorni.

Dopo aver studiato O'Caml, penso che sia un linguaggio interessante che può aiutarti a costruire programmi che non oseresti nemmeno scrivere, diciamo, in C / C ++ / Java: ad esempio, dai un'occhiata a Frama-C .

Per una descrizione aggiornata di O'Caml, ti incoraggio a leggere le sue caratteristiche : il linguaggio promuove tecniche di controllo statico del tipo che consentono alle implementazioni di concentrarsi sulla produzione di runtime performanti, ma sicuri.

Importante : non sono un esperto di OCaml: se sei uno di loro e vedi che ho scritto qualcosa di orribilmente sbagliato, per favore correggimi. Modifica questo post di conseguenza.

Controllo statico del tipo

  • Falso senso di sicurezza

    Questo è vero, ma ovvio.

    La digitazione statica fornisce prove di cui ti puoi fidare su un sottoinsieme delle proprietà del tuo programma. A meno che tu non accetti di diventare formale, un programma medio (non giocattolo) sarà soggetto a errori di programmazione che possono essere osservati solo in fase di esecuzione.

    Questo è quando possono essere applicate tecniche di controllo dinamico: il compilatore OCaml ha flag per generare file eseguibili con informazioni di debug e così via ... Oppure, può generare codice che si fida ciecamente del programmatore e cancella il più possibile le informazioni sul tipo. I programmatori che desiderano programmi efficaci devono implementare esplicitamente i controlli dinamici.

    Lo stesso vale per esempio Common Lisp, ma invertito: prima i tipi dinamici, con le dichiarazioni di tipo opzionali e le direttive del compilatore in secondo luogo.

  • Pochi tipi di base

    Si applica ancora: la lingua principale non è cambiata (o non in modo drammatico).

  • Overflow intero silenzioso

    Questa è la norma nella maggior parte delle lingue in cui l'overflow di numeri interi viene controllato manualmente. Non conosco alcuna libreria che verifichi le operazioni di controllo del tipo per verificare se può verificarsi un overflow.

  • Immutabilità del modulo

    1. L'autore menziona Functors ma non riesco a vedere come il suo esempio non possa essere implementato. Leggendo il capitolo Moduli di prima classe di https://realworldocaml.org , sembra che i moduli possano essere usati per comporre e costruire nuovi moduli. Naturalmente, la modifica di un modulo esistente richiede la modifica del codice sorgente, ma ancora una volta, questo non è insolito tra i linguaggi di programmazione.

    2. " Semanticamente , le funzioni sono compilate INLINE"

    Il thread reddit sopra non è d'accordo, dicendo che l'associazione viene risolta al momento del collegamento. Tuttavia, questo è un dettaglio di implementazione e penso che l'enfatizzato Semanticamente si riferisca al modo in cui le funzioni vengono risolte. Esempio:

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

    Il programma precedente viene compilato e, una volta eseguito, restituisce 5, poiché gè definito con la prima versione di f, proprio come se la funzione chiamante gavesse incorporato la chiamata f. Questo non è "cattivo", tra l'altro, è solo coerente con le regole di oscuramento del nome di O'Caml.

    Riassumendo : sì, i moduli sono immutabili . Ma sono anche componibili .

  • Il polimorfismo causa errori di tipo runtime

    Non riesco a riprodurre l'errore menzionato. Sospetto che sia un errore del compilatore.

Nessuna macro

In effetti, non ci sono macro ma preprocessori (OcamlP4, OcamlP5, ...).

Minor Suckiness della lingua

  • Registra un campo che dà un nome all'inferno

    Vero, ma dovresti usare i moduli:

    1. Due campi di due record hanno la stessa etichetta in OCaml
    2. Risoluzione dei nomi dei campi
  • Sintassi

    Si applica ancora (ma in realtà, questa è solo sintassi).

  • Nessun polimorfismo

    Si applica ancora, ma in qualche modo ci sono persone che preferiscono questo invece della torre numerica di Lisp (non so perché). Suppongo che aiuti con l'inferenza del tipo.

  • Set di funzioni incoerenti

    Vedi il progetto OCaml Batteries Included . In particolare, BatArray , per un esempio di map2array.

  • Nessuna variabile dinamica

    Può essere implementato:

    1. http://okmij.org/ftp/ML/dynvar.txt
    2. http://okmij.org/ftp/ML/index.html#dynvar
  • Gli argomenti ~ facoltativi fanno schifo

    Per restrizione linguistica, non è possibile combinare argomenti facoltativi e parole chiave in Common Lisp. Significa che fa schifo? (ovviamente, questo può essere modificato con le macro (vedi ad esempio la mia risposta )). Vedi la documentazione di O'Caml per argomenti facoltativi e nominati in O'Caml.

  • Incoerenza dell'applicazione argomento parziale

    Non penso che questo sia davvero fastidioso in pratica.

  • Leggibilità dell'aritmetica

    Tiene, ma puoi usare R o Python per problemi numerici se preferisci.

  • Risoluzione silenziosa del conflitto di nomi

    Si applica ancora, ma si noti che questo è ben documentato.

  • Nessun oggetto input / output

    Si applica ancora.

Implementazione, biblioteche

Questi continuano a cambiare ogni giorno: non esiste una risposta definitiva.

Infine,

"Dovresti provare OCaml (o, meglio ancora, Haskell) anche se pensi che faccia schifo e non hai intenzione di usarlo. Senza di esso, la tua istruzione in Informatica è incompleta, proprio come è incompleta senza alcuni Lisp e C (o , meglio ancora, Assemblea) esposizione ".

... si applica ancora.


Grazie, darò un'occhiata ai link, ma non sto davvero chiedendo se tali opinioni siano giustificate. Chiedo se i fatti valgono ancora. Ad esempio: esiste un modo per stampare qualsiasi tipo di dati algebrico? È ancora preciso che gli overflow di numeri interi senza avvisi? Esiste oggi un modo per operare su file e smaltirli senza dover scrivere ogni volta che il boilerplate si occupa di chiudere i file sugli errori?
Andrea,

@Andrea Ho modificato la mia risposta.
coredump,

1
"... ci sono persone che preferiscono quella invece della torre numerica di Lisp (non so perché). Suppongo che aiuti con l'inferenza del tipo." Bingo. Il sistema di tipi SML e OCaml richiede che ogni espressione abbia un solo tipo. Il sovraccarico di operatori matematici di SML è un'eccezione integrata nel linguaggio. Questo vale anche per Haskell; abilitare quel tipo di sovraccarico era la motivazione dietro le classi di tipo. Il problema è che puoi avere solo un'istanza di classe di tipo per tipo. Inoltre, non è possibile convertire ciecamente un numero intero in un float di uguale dimensione: un float a 64 bit ha solo 54 bit di precisione.
Doval,

@Doval Che ne dici di qualcosa come digitare la torre numerica per la racchetta? Possiamo immaginare una libreria OCaml in grado di sfruttare classi di tipi o tipi di dati algebrici generalizzati (GADT) al fine di fornire operatori matematici polimorfici? Per quanto riguarda la conversione: non tutte le operazioni sono possibili, ma alcune sono e potrebbero essere digitate.
coredump,

2
@Doval: "Ri: operatori matematici polimorfici, non credo che ci sia alcun modo senza implementare le classi di tipi di Haskell". F # ha operatori matematici polimorfici senza classi di tipi.
Jon Harrop,

7

Visto che la pagina è piuttosto vecchia (2007): quali dei punti elenco elencati sono ancora veri oggi?

  • Falso senso di sicurezza . Questo non ha senso.

  • Pochi tipi di base . OCaml ora ha byte e array di byte ma nessuna stringa unicode integrata, numeri interi a 16 bit, numeri interi senza segno, float a 32 bit, vettori o matrici. Le librerie di terze parti forniscono alcune di queste.

  • Overflow intero silenzioso . Invariato ma non è mai stato un problema.

  • Immutabilità del modulo . La sua raccomandazione che funzioni e moduli dovrebbero essere mutabili è un triste ritorno al Lisp e una pessima idea. Puoi sostituire i moduli usando includese vuoi, ma non puoi mutarli, ovviamente.

  • Il polimorfismo causa errori di tipo runtime . Questo è un grosso problema con OCaml e non è stato risolto. Man mano che i tuoi tipi evolvono l'uguaglianza polimorfica, il confronto e l'hashing falliranno quando incontrano tipi come funzioni e il debug del problema è molto difficile. F # ha un'ottima soluzione a questo problema.

  • Nessuna macro . Ironia della sorte, quando ha scritto questo OCaml in realtà aveva il pieno supporto per le macro ma ora hanno deciso di estrarre la funzionalità.

  • Wrapper . Questo è stato un vero problema e non è stato risolto. Non esiste ancora alcun try ... finallycostrutto nel linguaggio OCaml e nessun wrapper che lo implementa nello stdlib.

  • Luoghi . Invariato ma non problematico.

  • Registra un campo che dà un nome all'inferno . Strutturare correttamente il codice utilizzando i moduli.

  • Sintassi . Invariato ma non problematico.

  • Nessun polimorfismo . Questo è stato per lo più senza senso quando lo ha scritto e nulla è cambiato.

  • Set di funzioni incoerenti . OCaml non ha ancora una consfunzione. Va bene. Non voglio roba di Lisp nella mia lingua, grazie.

  • Nessuna variabile dinamica . È stata una buona cosa per OCaml. È ancora una buona cosa su OCaml.

  • Gli argomenti ~ facoltativi fanno schifo . Argomenti opzionali rock. Ho provato a Microsoft per convincerli ad aggiungere argomenti opzionali a F #.

  • Incoerenza dell'applicazione argomento parziale . Eh?

  • Leggibilità dell'aritmetica . Questo è cambiato da quando ho smesso di usare OCaml ~ 8 anni fa. Apparentemente ora puoi farlo Int64.((q * n - s * s) / (n - 1L)).

  • Risoluzione silenziosa del conflitto di nomi . Stava cercando di fare un vero e proprio sviluppo software nel REPL come faresti con Lisp. Non farlo in OCaml. Utilizzare file e compilazione batch ricorrendo al REPL solo per test, esecuzione di codice usa e getta e elaborazione tecnica interattiva.

  • Ordine di valutazione . Questo era sbagliato quando lo scrisse. L'ordine di valutazione non è definito in OCaml.

  • Nessun oggetto input / output . Ha citato una biblioteca di terze parti che ha già risolto questo "problema".

  • Il compilatore si arresta dopo il primo errore . Eh?

  • Nessuna traccia di stack per eseguibili compilati in modo nativo . Fisso.

  • Il debugger fa schifo . Non ho mai usato il debugger. Il controllo statico del tipo cattura quasi tutti i miei bug.

  • GC fa schifo . Ho trovato il GC di OCaml superbo, tranne per un grosso problema: il blocco globale impedisce la programmazione parallela.

  • Nessuna dichiarazione a termine implicita . La ricorsione reciproca è esplicita in base alla progettazione in tutte le ML. L'unica stranezza è che le typedefinizioni sono ricorsive per impostazione predefinita, mentre i letbinding non sono ricorsivi per impostazione predefinita.

  • Il round di funzioni è assente . OCaml ha ancora uno stile semplice ma le librerie di terze parti come Jane St's Core forniscono rounde amici.

  • Liste . List.mapnon è ancora ricorsivo alla coda. Ho inviato patch per correggere bug gravi come questo e ho dovuto aspettare anni prima che apparissero nelle versioni. Le liste sono ancora immutabili, ovviamente. E così dovrebbero essere.

  • Velocità . Credo che i tempi di compilazione per le grandi varianti polimorfiche siano stati corretti.

  • Corrispondenza del modello . Un trionfo di speranza sulla realtà. La comunità Lisp non è riuscita a farlo. Da qui la mia decima regola: qualsiasi programma Lisp sufficientemente complicato contiene un'implementazione ad hoc, specificata in modo informale e piena di bug di metà del compilatore di pattern match di OCaml.

Ad esempio: è ancora vero che è impossibile stampare un oggetto generico?

Quando ha scritto che non si poteva semplicemente fare:

print value

ma potresti invocare la bella stampante dal livello più alto come una chiamata in libreria, dandogli le informazioni necessarie sul tipo. E c'era una macro che è possibile utilizzare per annotare le strutture di dati al fine di avere le stampanti piuttosto autogenerate.


Corrispondenza dei motivi: il codice sorgente OCaml e optima fanno entrambi riferimento allo stesso documento: "Ottimizzazione della corrispondenza dei motivi". Direi che né "ad-hoc", "corretti da bug" né "specificati in modo informale" possono realisticamente essere applicati qui. "F # ha un'ottima soluzione a questo problema": mi piacerebbe davvero vedere qualche dettaglio in più su questo, se possibile. La risposta è buona ma imprecare quando si parla di consun brutto tono (l'articolo originale è un rant, ma non è necessario copiarlo da esso).
coredump,

2
@coredump: "Mi piacerebbe davvero vedere qualche dettaglio in più su questo, se possibile". F # ha un polimorfismo ad hoc definito dall'utente su determinati operatori e funzioni. Quindi puoi usare +ints, float e complessi ma puoi anche definire i tuoi tipi e aggiungere un sovraccarico per +lavorare sul tuo tipo. Ciò fornisce la brevità e la leggibilità di Lisp o Haskell con le prestazioni prevedibilmente buone di SML o OCaml, ottenendo qualcosa che nessun'altra lingua fa.
Jon Harrop l'

@coredump: "Il codice sorgente OCaml e optima fanno entrambi riferimento allo stesso documento". Le tecniche descritte in quel documento sono interamente basate sul sistema di tipo ML. Un sistema di tipi che Lisp non ha. Optima non fa e non può fare molto di ciò che è descritto in quel documento. Basta guardare la sezione 4.2 "Uso delle informazioni sull'esaustività". Non ci sono informazioni esaustive in Lisp perché non ci sono tipi di varianti. Ad esempio, OCaml sceglie tra i salti nidificati o una tabella di invio in base al numero di foglie ma queste informazioni sono sconosciute in Lisp.
Jon Harrop il

Lisp e OCaml sono progettati per cose diverse (es. Dinamismo, programmazione basata su immagini). Ma Lisp ha un sistema di tipi, diverso da Hindley-Milner, e le implementazioni lo sfruttano durante la compilazione. Guarda questa sessione SBCL con esempi tratti dalla sezione 4.2 del documento. L'esaustività è già verificata dal compilatore in base all'inferenza del tipo e alle dichiarazioni. Optima potrebbe aggiungere un codice specifico per l'implementazione per la macroespansione in fase di compilazione (o VOP SBCL), per avere altre strategie, ma non ci sono incentivi sufficienti per farlo.
coredump,

Come si applica a un tipo di dati algebrico definito dall'utente?
Jon Harrop,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.