Critique de l'OCaml: est-elle toujours valable?

15

Je suis un débutant complet avec OCaml. Je suis récemment tombé sur cette page listant une bonne quantité de critiques envers OCaml.

Voyant que la page est assez ancienne (2007): quels sont les points de puces énumérés qui sont toujours d'actualité? Par exemple: est-il toujours vrai qu'il est impossible d'imprimer un objet générique?

Je tiens à préciser que je ne cherche pas à discuter des opinions qui y sont exprimées. Je demande si les informations répertoriées, telles que le fait que les entiers débordent sans avertissements, sont toujours correctes pour les versions plus récentes d'OCaml

Andrea
la source
5
Je vote pour fermer cette question comme hors sujet parce que meta.programmers.stackexchange.com/questions/6417/…
Philipp
3
Il y a une fourchette où ceux-ci pourraient avoir été corrigés: tryfsharp.org
Den
7
Bien sûr, l'opinion dans cet essai que vous avez lié est valide, mais la pertinence de ces critiques pour vous est une tout autre affaire. Notez que l'auteur provient d'un arrière-plan Common Lisp et a donc des valeurs Lispish. Si vous adoptez une autre approche d'OCaml que de la comparer à Lisp (par exemple, «OCaml est Haskell pour les simples mortels» ou «Si C était un langage fonctionnel, vous auriez OCaml»), vous le trouverez bien plus satisfaisant. Malgré tous ses défauts, OCaml est un excellent langage et je vous encourage néanmoins à y essayer.
amon
5
Autant que je sache, il n'y a aucun moyen de détecter un débordement d'entier dans le matériel , vous devez donc insérer des vérifications d'exécution partout pour le détecter; mais l'auteur a rejeté l'utilisation de "bignum" sur la base de la performance! La plainte concernant la saisie statique revient à dire que les ceintures de sécurité sont mauvaises parce que vous pourriez penser que vous ne pouvez pas mourir dans un accident de voiture. La plainte concernant l'immuabilité du module dit qu'il veut faire du monkeypatch - une pratique anti-modulaire et sujette aux erreurs. Le "petit zoo de type" n'a rien à voir avec l'inférence de type. Il est clair où se trouvent ses préjugés .
Doval
2
@Doval: Bien sûr, vous pouvez détecter un débordement dans le matériel. Le traiter, cependant, est une affaire de logiciel.
Mason Wheeler

Réponses:

13

Cet article est discuté à plusieurs endroits:

Pour résumer: oui, OCaml n'est pas un Lisp, et non, il n'est pas parfait (qu'est-ce que ça veut dire?). Je ne pense pas que les points mentionnés dans le blog soient pertinents pour les programmeurs O'Caml au quotidien.

Après avoir étudié O'Caml, je pense que c'est un langage intéressant qui peut vous aider à créer des programmes dans lesquels vous n'oseriez même pas écrire, disons C / C ++ / Java: par exemple, jetez un œil à Frama-C .

Pour une description à jour d'O'Caml, je vous encourage à lire sur ses caractéristiques : le langage promeut des techniques de vérification de type statique fortes qui permettent aux implémentations de se concentrer sur la production d'exécutions performantes, mais sûres.

Important : je ne suis pas un expert OCaml: si vous êtes l'un d'eux et voyez que j'ai écrit quelque chose d'horriblement mal, veuillez me corriger. Je modifierai ce message en conséquence.

Vérification de type statique

  • Faux sentiment de sécurité

    C'est vrai, mais évident.

    La saisie statique vous donne des preuves fiables sur un sous-ensemble des propriétés de votre programme. Sauf si vous acceptez de passer à la formalité, un programme moyen (non jouet) sera soumis à des bogues d'erreur de programmation qui ne peuvent être constatés qu'au moment de l'exécution.

    C'est à ce moment que les techniques de vérification dynamique peuvent être appliquées: le compilateur OCaml a des drapeaux pour générer des exécutables avec des informations de débogage, et ainsi de suite ... Ou, il peut générer du code qui fait aveuglément confiance au programmeur et efface autant que possible les informations de type. Les programmeurs qui souhaitent des programmes robustes doivent implémenter explicitement les vérifications dynamiques.

    La même chose s'applique par exemple à Common Lisp, mais inversé: les types dynamiques en premier, avec les déclarations de type optionnelles et les directives du compilateur en second.

  • Quelques types de base

    S'applique toujours: le langage de base n'a pas changé (ou pas radicalement).

  • Débordement d'entier silencieux

    C'est la norme dans la plupart des langues que le débordement d'entier est vérifié à la main. Je ne connais aucune bibliothèque qui effectuerait des vérifications de type pour vérifier si un débordement peut se produire.

  • Immuabilité du module

    1. L'auteur mentionne Functors mais je ne vois pas comment son exemple ne peut pas être implémenté. En lisant le chapitre Modules de première classe de https://realworldocaml.org , il semble que les modules peuvent être utilisés pour composer et construire de nouveaux modules. Bien sûr, la modification d'un module existant nécessite une modification du code source, mais encore une fois, ce n'est pas inhabituel parmi les langages de programmation.

    2. " Sémantiquement , les fonctions sont compilées EN LIGNE"

    Le fil reddit ci-dessus n'est pas d'accord, disant que la liaison est résolue au moment du lien. Cependant, c'est un détail d'implémentation et je pense que l'accent sémantique se rapporte à la façon dont les fonctions sont résolues. Exemple:

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

    Le programme ci-dessus compile et, lorsqu'il est exécuté, renvoie 5, car il gest défini avec la première version de f, comme si la fonction appelante galignait l'appel à f. Ce n'est pas "mauvais", soit dit en passant, c'est juste cohérent avec les règles d'occultation du nom d'O'Caml.

    Pour résumer : oui, les modules sont immuables . Mais ils sont également composables .

  • Le polymorphisme provoque des erreurs de type d'exécution

    Je ne peux pas reproduire l'erreur mentionnée. Je soupçonne que c'est une erreur de compilation.

Pas de macros

En effet, il n'y a pas de macros mais des préprocesseurs (OcamlP4, OcamlP5, ...).

Suckiness langue mineure

  • L'enfer des noms de champ d'enregistrement

    Vrai, mais vous devez utiliser des modules:

    1. Deux champs de deux enregistrements ont la même étiquette dans OCaml
    2. Résolution des noms de champ
  • Syntaxe

    S'applique toujours (mais vraiment, ce n'est qu'une syntaxe).

  • Pas de polymorphisme

    S'applique toujours, mais d'une certaine façon, il y a des gens qui préfèrent cela au lieu de la tour numérique de Lisp (je ne sais pas pourquoi). Je suppose que cela aide à l'inférence de type.

  • Ensembles de fonctions incohérents

    Voir le projet OCaml Batteries Inclus . En particulier, BatArray , pour un exemple de map2pour les tableaux.

  • Pas de variables dynamiques

    Peut être implémenté:

    1. http://okmij.org/ftp/ML/dynvar.txt
    2. http://okmij.org/ftp/ML/index.html#dynvar
  • Facultatif ~ les arguments sont nuls

    Par restriction de langue, vous ne pouvez pas mélanger des arguments facultatifs et des mots clés en Common Lisp. Est-ce à dire que ça craint? (bien sûr, cela peut être changé avec des macros (voir par exemple ma réponse )). Voir la documentation d' O'Caml pour les arguments facultatifs et nommés dans O'Caml.

  • Incohérence partielle de l'application d'argument

    Je ne pense pas que ce soit vraiment ennuyeux dans la pratique.

  • Lisibilité de l'arithmétique

    C'est vrai, mais vous pouvez utiliser R ou Python pour les problèmes numériques si vous préférez.

  • Résolution silencieuse des conflits de noms

    S'applique toujours, mais notez que cela est bien documenté.

  • Pas d'entrée / sortie d'objet

    S'applique toujours.

Implémentation, bibliothèques

Celles-ci changent tous les jours: il n'y a pas de réponse définitive.

Finalement,

"Vous devriez essayer OCaml (ou, mieux encore, Haskell) même si vous pensez que ça craint et que vous ne prévoyez pas de l'utiliser. Sans lui, votre formation en informatique est incomplète, tout comme elle est incomplète sans quelques Lisp et C (ou , mieux encore, Assemblée) exposition. "

... s'applique toujours.

coredump
la source
Merci, je vais regarder les liens, mais je ne demande pas vraiment si ces opinions sont justifiées. Je demande si les faits sont toujours valables. Par exemple: existe-t-il un moyen d'imprimer un type de données algébrique? Est-il toujours exact que les entiers débordent sans avertissements? Existe-t-il aujourd'hui un moyen d'opérer sur fichier et de les disposer sans avoir à écrire à chaque fois le passe-partout pour faire face à la fermeture des fichiers en cas d'erreur?
Andrea
@Andrea j'ai modifié ma réponse.
coredump
1
"... il y a des gens qui préfèrent cela au lieu de la tour numérique de Lisp (je ne sais pas pourquoi). Je suppose que cela aide à l'inférence de type." Bingo. Le système de types SML et OCaml requiert que chaque expression ait un seul type. La surcharge SML d'opérateurs mathématiques est une exception intégrée au langage. Cela vaut également pour Haskell; permettre ce type de surcharge était la motivation derrière les classes de type. Le hic est que vous ne pouvez avoir qu'une seule instance de classe de type par type. Vous ne pouvez pas non plus convertir aveuglément un entier en un flottant de taille égale - un flottant 64 bits n'a que 54 bits de précision.
Doval
@Doval Qu'en est-il de quelque chose comme Taper la tour numérique pour la raquette? Peut-on imaginer une bibliothèque OCaml qui exploiterait des classes de types ou des types de données algébriques généralisées (GADT) afin de fournir des opérateurs mathématiques polymorphes? Concernant la conversion: toutes les opérations ne sont pas possibles, mais certaines sont et pourraient être tapées.
coredump
2
@Doval: "Re: opérateurs mathématiques polymorphes, je ne pense pas qu'il y ait un moyen sans implémenter les classes de types de Haskell". F # a des opérateurs mathématiques polymorphes sans classes de types.
Jon Harrop du
7

Voyant que la page est assez ancienne (2007): quels sont les points de puces énumérés qui sont toujours d'actualité?

  • Faux sentiment de sécurité . Ça n'a pas de sens.

  • Quelques types de base . OCaml a maintenant des octets et des tableaux d'octets mais pas de chaînes Unicode intégrées, d'entiers 16 bits, d'entiers non signés, de flottants 32 bits, de vecteurs ou de matrices. Des bibliothèques tierces en fournissent certaines.

  • Débordement d'entier silencieux . Inchangé mais ce n'était jamais un problème.

  • Immuabilité du module . Sa recommandation que les fonctions et les modules devraient être mutables est un sombre retour à Lisp et une très mauvaise idée. Vous pouvez remplacer les modules en utilisant includesi vous le souhaitez, mais vous ne pouvez pas les muter, bien sûr.

  • Le polymorphisme provoque des erreurs de type au moment de l'exécution . C'est un gros problème avec OCaml et il n'a pas été corrigé. À mesure que vos types évoluent vers l'égalité polymorphe, la comparaison et le hachage commencent à échouer lorsqu'ils rencontrent des types tels que des fonctions et le débogage du problème est très difficile. F # a une excellente solution à ce problème.

  • Pas de macros . Ironiquement, quand il a écrit cet OCaml avait en fait un support complet pour les macros, mais ils ont maintenant décidé de retirer la fonctionnalité.

  • Emballages . C'était un vrai problème et il n'a pas été résolu. Il n'y a toujours pas de try ... finallyconstruction dans le langage OCaml et aucun wrapper l'implémentant dans stdlib.

  • Des lieux . Inchangé mais sans problème.

  • Enregistrer l'enfer de nommage sur le terrain . Structurez correctement votre code à l'aide de modules.

  • Syntaxe . Inchangé mais sans problème.

  • Pas de polymorphisme . C'était surtout un non-sens quand il l'a écrit et rien n'a changé.

  • Ensembles de fonctions incohérents . OCaml n'a toujours pas de consfonction. C'est très bien. Je ne veux pas de trucs Lisp dans ma langue, merci.

  • Pas de variables dynamiques . C'était une bonne chose à propos d'OCaml. C'est toujours une bonne chose à propos d'OCaml.

  • Les arguments facultatifs sont nulles . Arguments optionnels rock. J'ai harcelé Microsoft pour lui faire ajouter des arguments facultatifs à F #.

  • Incohérence partielle de l'application d'argument . Eh?

  • Lisibilité de l'arithmétique . Cela a changé depuis que j'ai cessé d'utiliser OCaml il y a environ 8 ans. Apparemment, vous pouvez maintenant le faire Int64.((q * n - s * s) / (n - 1L)).

  • Résolution silencieuse des conflits de noms . Il essayait de faire un développement logiciel complet dans le REPL comme vous le feriez dans Lisp. Ne faites pas cela dans OCaml. Utilisez les fichiers et la compilation par lots ayant recours au REPL uniquement pour les tests, l'exécution de code jetable et l'informatique technique interactive.

  • Ordre d'évaluation . C'était mal quand il l'a écrit. L'ordre d'évaluation n'est pas défini dans OCaml.

  • Aucun objet entrée / sortie . Il a cité une bibliothèque tierce qui avait déjà résolu ce "problème".

  • Le compilateur s'arrête après la première erreur . Eh?

  • Aucune trace de pile pour les exécutables compilés en mode natif . Fixé.

  • Le débogueur craint . Je n'ai jamais utilisé le débogueur. La vérification de type statique attrape presque tous mes bogues.

  • GC craint . J'ai trouvé que le GC d'OCaml était superbe à l'exception d'un problème majeur: le verrouillage global empêche la programmation parallèle.

  • Pas de déclarations directes implicites . La récursion mutuelle est explicite par conception dans tous les ML. La seule bizarrerie est que les typedéfinitions sont récursives par défaut tandis que les letliaisons sont non récursives par défaut.

  • La fonction ronde est absente . OCaml a toujours un stdlib simple mais des bibliothèques tierces comme Jane St's Core fournissent roundet amis.

  • Listes . List.mapn'est toujours pas récursif de queue. J'ai soumis des correctifs pour corriger de graves bogues comme celui-ci et j'ai dû attendre des années avant qu'ils n'apparaissent dans les versions. Les listes sont toujours immuables, bien sûr. Et ils devraient l'être.

  • La vitesse . Je crois que les temps de compilation pour les grandes variantes polymorphes ont été corrigés.

  • Correspondance de motif . Un triomphe d'espoir sur la réalité. La communauté Lisp n'a pas réussi à le faire. D'où ma 10ème règle: tout programme Lisp suffisamment compliqué contient une implémentation ad hoc, spécifiée de manière informelle et contenant des bogues de la moitié du compilateur de correspondance de modèle d'OCaml.

Par exemple: est-il toujours vrai qu'il est impossible d'imprimer un objet générique?

Quand il a écrit que vous ne pouviez pas simplement faire:

print value

mais vous pouvez appeler la jolie imprimante du niveau supérieur en tant qu'appel de bibliothèque, en lui donnant les informations de type nécessaires. Et il y avait une macro que vous pouvez utiliser pour annoter les structures de données afin d'avoir de jolies imprimantes générées automatiquement.

Jon Harrop
la source
Correspondance de modèle: code source OCaml et optima les deux références du même article: "Optimisation Pattern Matching". Je dirais que ni "ad-hoc", "bug-ridden" ni "informally-specified" ne peuvent être appliqués de manière réaliste ici. "F # a une excellente solution à ce problème": j'aimerais vraiment voir un peu plus de détails à ce sujet, si possible. La réponse est bonne mais maudire en parlant consdonne un mauvais ton (l'article original est une diatribe, mais vous n'avez pas besoin de le copier).
coredump
2
@coredump: "J'aimerais vraiment voir un peu plus de détails à ce sujet, si possible". F # a un polymorphisme ad hoc défini par l'utilisateur sur certains opérateurs et fonctions. Vous pouvez donc l'utiliser +sur des entiers, des flottants et des complexes, mais vous pouvez également définir vos propres types et ajouter une surcharge pour +travailler sur votre type. Cela fournit la brièveté et la lisibilité de Lisp ou Haskell avec les bonnes performances prévisibles de SML ou OCaml, réalisant quelque chose qu'aucun autre langage ne fait.
Jon Harrop
@coredump: "Le code source OCaml et optima font tous deux référence au même document". Les techniques décrites dans cet article reposent entièrement sur le système de type ML. Un système de type que Lisp n'a pas. Optima ne fait pas et ne peut pas faire beaucoup de ce qui est décrit dans cet article. Regardez simplement la section 4.2 «Utilisation des informations d'exhaustivité». Il n'y a pas d'informations exhaustives dans Lisp car il n'y a pas de types de variantes. Par exemple, OCaml choisit entre des sauts imbriqués ou une table de répartition en fonction du nombre de feuilles mais cette information est inconnue dans Lisp.
Jon Harrop
Lisp et OCaml sont conçus pour différentes choses (par exemple, le dynamisme, la programmation basée sur l'image). Mais Lisp a un système de type différent de Hindley-Milner, et mises en œuvre ne l' exploiter lors de la compilation. Regardez cette session SBCL avec des exemples de la section 4.2 de l'article. L'exhaustivité est déjà vérifiée par le compilateur à partir de l'inférence de type et des déclarations. Optima pourrait ajouter du code spécifique à l'implémentation pour la macroexpansion à la compilation (ou les VOP SBCL), pour avoir d'autres stratégies, mais il n'y a pas suffisamment d'incitations pour le faire.
coredump
Comment cela s'applique-t-il au type de données algébrique défini par l'utilisateur?
Jon Harrop