Objective Caml

Infos
Objective Caml (OCaml) est la principale implémentation du langage de programmation Caml, créé par Xavier Leroy, Jérôme Vouillon, Damien Doligez, Didier Rémy et leurs collaborateurs en 1996. Ce langage, de la famille des langages ML, est un projet open source dirigé et maintenu essentiellement par l'INRIA. OCaml est le successeur de Caml Light, auquel il a ajouté entre autres une couche de programmation objet. L'acronyme CAML provient de Categorical Abstract Machine
Objective Caml

Objective Caml (OCaml) est la principale implémentation du langage de programmation Caml, créé par Xavier Leroy, Jérôme Vouillon, Damien Doligez, Didier Rémy et leurs collaborateurs en 1996. Ce langage, de la famille des langages ML, est un projet open source dirigé et maintenu essentiellement par l'INRIA. OCaml est le successeur de Caml Light, auquel il a ajouté entre autres une couche de programmation objet. L'acronyme CAML provient de Categorical Abstract Machine Language, mais les versions récentes de Caml ont abandonné la machine abstraite.

Principes

Caml est un langage fonctionnel augmenté de fonctionnalités permettant la programmation impérative. Objective Caml étend les possibilités du langage en permettant la programmation orientée objet et la programmation modulaire. Pour toutes ces raisons, OCaml entre dans la catégorie des langages multi-paradigme. Il intègre ces différents concepts dans un système de types hérité de ML, caractérisé par un typage statique, fort et inféré. Le système de types permet une manipulation aisée de structures de données complexes : on peut aisément représenter des types algébriques, c'est à dire des types hiérarchisés et potentiellement récursifs (listes, arbres...), et les manipuler aisément à l'aide du filtrage par motif. Cela fait de OCaml un langage de choix dans les domaines demandant la manipulation de structures de données complexes, par exemple les compilateurs. Le typage fort, ainsi que l'absence de manipulation explicite de la mémoire (présence d'un ramasse-miettes) font de OCaml un langage très sûr. Il est aussi réputé pour ses performances, grâce à la présence d'un compilateur de code natif.

Histoire

Le langage Caml est né de la rencontre du langage de programmation ML, auquel s'est intéressé l'équipe Formel de l'INRIA depuis le début des années 1980, et de la machine abstraite catégorique CAM de Guy Cousineau, fondée sur les travaux de Pierre-Louis Curien en 1984. La première implantation, écrite par Ascander Suarez puis maintenue par Pierre Weis et Michel Mauny, a été publiée en 1987. Le langage s'est peu à peu différencié de son père ML car l'équipe de l'INRIA voulait adapter un langage à ses propres besoins, et continuer à le faire évoluer, ce qui entrait en conflit avec la "stabilité" de ML imposée par les efforts de standardisation de Standard ML. Les limitations de la CAM ont conduit à la création d'une nouvelle implantation, mise au point par Xavier Leroy dès 1990, sous le nom de Caml light. Cette implantation, dont une version récente est encore utilisée dans l'enseignement de nos jours, bien que le système ne soit plus maintenu par l'INRIA, fonctionne grâce à un interpréteur de code octet (bytecode) codé en C, qui lui assure une grande portabilité. Le système de gestion de la mémoire, conçu par Damien Doligez, a aussi fait son apparition dans Caml Light. En 1995, Xavier Leroy publie une version de Caml nommée Caml Special Light, qui introduit un compilateur de code natif, et un système de modules inspiré des modules de Standard ML. Objective Caml, publié pour la première fois en 1996, apporte à Caml un système objet conçu par Didier Rémy et Jérôme Vouillon. Certaines fonctionnalités avancées, comme les variantes polymorphes ou les labels (permettant de donner des noms aux arguments des fonctions) ont été introduites en 2000 par Jacques Garrigue. Objective Caml s'est relativement stabilisé depuis (malgré l'absence d'une spécification, le document en vigueur étant le manuel officiel maintenu par l'INRIA). De nombreux dialectes de OCaml sont apparus, et continuent d'explorer des aspects spécifiques des langages de programmation (concurrence, parallèlisme, évaluation paresseuse, intégration du XML...) ; voir la section "Langages dérivés".

Principales caractéristiques

Langage fonctionnel

OCaml possède la plupart des caractéristiques communes des langages fonctionnels, en particulier des fonctions d'ordre supérieur et fermetures (closures), et un bon support de la récursion terminale.

Typage

Le typage statique d'OCaml détecte au moment de la compilation un grand nombre d'erreurs de programmation qui pourraient poser des problèmes au moment de l'exécution. Toutefois, contrairement à la plupart des autres langages, il n'est pas nécessaire de préciser le type des variables que l'on utilise. En effet, Caml dispose d'un algorithme de synthèse de types qui lui permet de déterminer le type des variables à partir du contexte dans lequel elles sont employées. Ainsi, la déclaration let division_entiere x y = x mod y introduit une fonction, division_entiere, à deux arguments, ici x et y, qui doivent tous deux être entiers, et le résultat de l'évaluation est toujours un entier. C'est ce qu'exprime OCaml en répondant automatiquement val division_entiere : int -> int -> int = Le système de typage ML supporte le polymorphisme paramétrique, c'est à dire des types dont des parties seront indéterminées au moment de la définition de la valeur. Cette fonctionnalité, automatique, permet d'obtenir des résultats comparables aux generics de Java ou C
- ou aux templates de C++. Ainsi, la déclaration let identite x = x introduit une fonction, identite, à un argument, ici x. Cet argument peut avoir n'importe quel type. Le résultat de l'évaluation est du même type que celui de l'argument. C'est ce qu'exprime OCaml en répondant automatiquement val identite : 'a -> 'a = Cependant, les extensions du typage ML requises par l'intégration de fonctionnalités avancées, comme la programmation orientée objet, complexifie dans certains cas le système de types : l'utilisation de ces fonctionnalités peut alors demander un temps d'apprentissage au programmeur, qui n'est pas forcément familier des systèmes de types sophistiqués.

Filtrage

Le filtrage par motif (pattern matching) est un élément essentiel du langage Caml. Il permet d'alléger le code grâce à une écriture plus souple que des conditions classiques, et l'exhaustivité fait l'objet d'une vérification : le compilateur propose un contre-exemple lorsqu'un filtrage incomplet est décelé. Par exemple, le code suivant provoque une erreur : type etat = Actif | Inactif | Inconnu (
- type somme : un état est l'une des trois valeurs : Actif, Inactif, Inconnu
-) let est_actif = function | Actif -> true | Inactif -> false Warning P: this pattern-matching is not exhaustive. Here is an example of a value that is not matched: Inconnu

Modules

Les modules permettent de décomposer le programme en une hiérarchie de structures contenant des types et des valeurs logiquement reliés entre eux (par exemple, toutes les fonctions de manipulation de listes vont dans le module List). Les descendants de la famille ML sont les langages ayant actuellement les systèmes de modules les plus perfectionnés, qui permettent, en plus de disposer d'espaces de noms, de mettre en oeuvre l'abstraction (valeurs accessibles dont l'implémentation est cachée) et la composabilité (valeurs qui peuvent être construires par dessus différents modules, du moment qu'ils répondent à une interface donnée). Ainsi, les trois unités syntaxiques de construction syntaxiques sont les structures, les interfaces et les modules. Les structures contiennent l'implémentation des modules, les interfaces décrivent les valeurs qui en sont accessibles (les valeurs dont l'implémentation n'est pas exposées sont des valeurs abstraites, et celles qui n'apparaissent pas du tout dans l'implémentation du module sont inaccessibles, à l'instar des méthodes privées en programmation orientée objet). Un module peut avoir plusieurs interfaces (du moment qu'elles sont toutes compatibles avec les types de l'implémentation), et plusieurs modules peuvent vérifier une même interface. Les foncteurs sont des structures paramétrées par d'autres structures; par exemple, les ensembles (module Set) de la bibliothèque standard OCaml sont implémentés comme un foncteur qui peut prendre en paramètre toute structure implémentant l'interface composée d'un type, et d'une fonction de comparaison entre les valeurs de ce type.

Orienté objet

OCaml se distingue particulièrement par son extension du typage ML vers un système objet comparable à ceux utilisés par les langages objets classiques. Cela permet un sous-typage structurel, dans lequel les objets sont de types compatibles si les types de leur méthodes sont compatibles, indépendamment de leurs arbres d'héritage respectifs. Cette fonctionnalité, tout à fait nouvelle dans les langages statiquement typés (on peut la considérer comme un équivalent du duck typing des langages dynamiques) permet une intégration naturelle des concepts objets dans un langage globalement fonctionnel.

Distribution

La distribution OCaml contient :
- Un interpréteur interactif (ocaml)
- Un compilateur bytecode (ocamlc) et l'interpréteur de bytecode (ocamlrun)
- Un compilateur natif (ocamlopt)
- Des générateurs d'analyseurs lexicaux (ocamllex) et syntaxiques (ocamlyacc),
- Un préprocesseur (camlp4), qui permet des extensions ou modifications de la syntaxe du langage
- Un débogueur pas à pas, avec retour en arrière (ocamldebug)
- Des outils de profiling
- Un générateur de documentation (ocamldoc)
- Un gestionnaire de compilation automatique (ocamlbuild), depuis OCaml 3.10,
- Une bibliothèque standard variée Les outils OCaml sont régulièrement utilisés sous Windows, Linux ou Mac OS. Le compilateur bytecode permet de créer des fichiers qui sont ensuite interprétés par ocamlrun. Le bytecode étant indépendant de la plateforme, cela assure une grande portabilité (ocamlrun pouvant à priori être compilé sur toute plateforme supportant un compilateur C fonctionnel). Le compilateur natif produit un code assembleur spécifique à la plateforme, ce qui sacrifie la portabilité pour des performances grandement améliorées. Un compilateur natif est présent pour les plateformes IA32, PowerPC, AMD64, Alpha, Sparc, Mips, IA64, HPPA et StrongArm. Une interface de compatibilité permet de lier du code OCaml à des primitives en C, et le format des tableaux de nombre flottants est compatible avec C et FORTRAN. OCaml permet aussi l'intégration de code OCaml dans un programme en C, ce qui permet de distribuer des bibliothèques OCaml à des programmeurs en C sans qu'ils aient besoin de connaître ou même d'installer OCaml. Les outils OCaml sont majoritairement codés en OCaml, à l'exception de quelques bibliothèques et de l'interpréteur bytecode, qui sont codés en C. En particulier, le compilateur natif est entièrement codé en OCaml.

Gestion de la mémoire

OCaml dispose, comme Java, d'une gestion automatisée de la mémoire, grâce à un ramasse-miettes incrémental générationnel. Celui-ci est spécialement adapté à un langage fonctionnel : « Les programmes fonctionnels sont des programmes qui allouent en général beaucoup et on constate que de très nombreuses valeurs ont une durée de vie très courte. D'autre part, dès qu'une valeur a survécu à plusieurs GC, elle a de grandes chances d'exister pour un bon moment » (optimisé pour un rythme rapide d'allocation/libération de petits objets), n'a donc pas d'impact sensible sur les performances des programmes. Il est configurable pour rester efficace dans des situations atypiques d'utilisation de la mémoire.

Performances

OCaml se distingue de la plupart des langages développés dans des milieux académiques par d'excellentes performances. En plus des optimisations locales "classiques" effectuées par le générateur de code natif, les performances profitent avantageusement de la nature fonctionnelle et fortement typée du langage. Ainsi, les informations de typage sont complètement déterminées à la compilation, et n'ont pas besoin d'être reproduites dans le code natif, ce qui permet entre autres de retirer complètement les tests de typage au moment de l'exécution. D'autre part, certains algorithmes de la bibliothèques standard exploitent les propriétés intéressantes des structures de données fonctionnelles pures : ainsi, l'algorithme d'union d'ensembles est asymptotiquement plus rapide que celui des langages impératifs, car il utilise leur non-mutabilité pour réutiliser une partie des ensembles de départ pour constituer l'ensemble de sortie (c'est la technique de path copying pour les structures de données persistentes). Historiquement, les langages fonctionnels ont été considérés comme naturellement lents par les programmeurs, mais les progrès des techniques de compilation ont permis de rattraper l'avantage initial des langage impératifs. OCaml, en optimisant efficacement « Guarantees provided by the type system can also enable powerful program optimizations. » ces parties du langage (boxing, closures...) et en implémentant un GC adapté aux allocations fréquentes des langages fonctionnels, a été un des premiers langages à démontrer l'efficacité retrouvée de la programmation fonctionnelle. En pratique, les performances sont en général légèrement inférieures à celle d'un code équivalent en C. Xavier Leroy parle prudemment de "performances d'au moins 50% celles d'un compilateur C raisonnable" : « our blanket performance statement "OCaml delivers at least 50% of the performance of a decent C compiler" is not invalidated :-) ». Ces prévisions ont depuis été confirmées par de nombreux benchmarks. En pratique, les programmes restent en général dans cette fourchette (de 1 à 2 fois celle du code C), avec des extrêmes dans les deux directions (parfois plus rapide que le C, parfois fortement ralenti par une interaction malheureuse avec le GC). Dans tous les cas, cela reste plus rapide que la plupart des langages récents qui ne sont pas compilés nativement, comme Python, Ruby ou même les langages de la plateforme .NET.

Utilisation

Le langage OCaml, issus des milieux de recherche, ne bénéficie pas de la puissance publicitaire de certains langages de programmation actuels. Il reste donc relativement peu connu du "grand public" informatique (ainsi que la plupart des langages fonctionnels), mais est cependant solidement implanté dans quelques niches dans lesquelles les qualités du langage contrebalancent son relatif manque de publicité et de support.

Enseignement

La nature multi-paradigme de OCaml et sa syntaxe légère en font un langage riche et apprécié par les enseignants, qui y voient un moyen d'initier leurs étudiants à différents aspects de la programmation dans un cadre unifié. En particulier, Caml (OCaml ou son petit frère Caml Light) est le langage utilisé par la plupart des classes préparatoires françaises, dans l'optique des épreuves d'informatique des concours d'entrée aux grandes écoles. Il est aussi utilisé dans de nombreuses universités, en France (pour des raisons historiques) mais aussi dans le reste du monde, par exemple les États-Unis ou le Japon. Il souffre dans le milieu universitaire de la concurrence avec son cousin éloigné Haskell, préféré dans certains cours de programmation fonctionnelle car il ne reprend aucun concept de la programmation impérative.

Recherche

OCaml est un langage assez utilisé dans le milieu de la recherche. Historiquement, les langages de la branche ML ont toujours été étroitement lié au domaine aux systèmes de preuves formelles (le ML initial de Robin Milner est ainsi apparu pour être utilisé dans le système de preuves LCF). OCaml est le langage utilisé par un des logiciels majeurs du domaine, l'assistant de preuves Coq. OCaml est bien évidemment présent dans de nombreux autres domaines de la recherches informatiques, dont la recherche en langages de programmation et compilateurs (voir la section "langages dérivés"), ou pour le logiciel de synchronisation de fichier Unison.

Industrie

Malgré sa communication relativement timide, OCaml a acquis une solide base d'utilisateurs dans des domaines spécifiques de l'industrie. Ainsi, l'industrie aéronautique utilise OCaml pour sa sûreté de programmation et son efficacité pour la formulation d'algorithmes complexes. On peut citer dans ce domaine le projet (Analyse Statique de logiciels Temps-RÉel Embarqués), utilisé entre autres par la compagnie Airbus. OCaml est utilisé par des acteurs importants de l'industrie du logiciel, comme Microsoft, Intel ou XenSource, tous trois membres du Consortium Caml. Il trouve aussi des applications dans l'informatique financière, comme l'a montré l'entreprise Jane Street Capitalconférence , qui emploie de nombreux programmeurs OCaml. Enfin, il est aussi utilisé par des projets libres généralistes, comme MLDonkey, GeneWeb, la bibliothèque , ou même certains logiciels de l'environnement de bureau KDE.

Présentation du langage

Déclarations et valeurs de base

Le code peut être entré simplement à la suite de l'invite "
-" qui figure en début de ligne. Par exemple, pour définir une variable x contenant le résultat du calcul 1 + 2
- 3, on écrira :
- let x = 1 + 2
- 3 ;; Après avoir saisi et validé cette expression, Caml détermine le type de l'expression (en l'occurrence, il s'agit d'un entier) et affiche le résultat du calcul : val x : int = 7 On peut être tenté d'effectuer toutes sortes de calculs. Cependant, il faut prendre garde à ne pas mélanger les entiers et les réels, ce que l'on fait couramment dans de nombreux langages, car ceci provoque une erreur lors de la compilation :
-2.3 + 1 ;; Characters 0 - 3: 2.3 + 1.;; ^^ This expression has type float but is here used with type int Cet exemple simple permet de se faire une première idée du fonctionnement de l'algorithme de synthèse de types. En effet, lorsque nous avons écrit "2.3 + 1", nous avons ajouté le réel 2.3 et l'entier 1, ce qui pose problème. En fait, pour effectuer ce calcul, nous devons nous assurer que tous les nombres ont le même type, d'une part, et employer la loi de composition interne + appliquée aux réels, notée "+." en Caml. Nous aurions donc dû écrire :
-2.3 +. 1.0 ;; - : float = 3.3

Fonctions

Les programmes sont souvent structurés en procédures et en fonctions. Les procédures sont composées d'un ensemble de commandes utilisées plusieurs fois dans le programme, et regroupées par commodité sous un même nom. Une procédure ne renvoie pas de valeur, ce rôle étant dévolu aux fonctions. De nombreux langages disposent de mots-clefs distincts pour introduire une nouvelle procédure ou une nouvelle fonction ("Procedure" et "Function" en Pascal, "Sub" et "Function" en Visual Basic, etc...). Caml, quant à lui, ne possède que des fonctions, et celles-ci se définissent de la même manière que les variables. Par exemple, pour définir l'identité, on peut écrire :
- let id = function x -> x ;; Après saisie et validation de l'expression, l'algorithme de synthèse de types détermine le type de la fonction. Cependant, dans l'exemple que nous avons donné, rien ne présage du type de x, aussi la fonction apparaît-elle comme polymorphe (à tout élément de l'ensemble 'a, elle associe une image id(x) qui est élément de l'ensemble 'a) : val id : 'a -> 'a =

Récursivité

La récursivité consiste à rédiger une fonction qui fait référence à elle-même, sur le modèle de la récurrence mathématique. En Caml, les fonctions récursives sont introduites à l'aide du mot-clef rec. Par exemple, pour définir la factorielle, nous pouvons écrire : let rec factorielle n = if n = 0 then 1 else n
- factorielle (n-1) ;;

Définitions internes

Il est possible de définir des variables ou des fonctions à l'intérieur d'une fonction. On utilise pour cela la syntaxe suivante :
-let factorielle n = let rec auxiliaire resultat = function | 0 -> resultat | n -> auxiliaire (n
- resultat) (n - 1) in auxiliaire 1 n ;; Cette écriture est un exemple de récursivité terminale. Le programme est analogue à une boucle, et est considéré comme tel par le compilateur (qui produira par exemple le code ASM d'une boucle, égalant ainsi les performances du code impératif correspondant).

Manipulation de listes

Les listes sont très utilisées en programmation, en particulier pour les traitements récursifs. Pour construire une liste, plusieurs écritures sont possibles :
-1 :: 2 :: 3 :: ;;
- ;; Ce faisant, on obtient une liste d'entiers, que Caml note de la façon suivante : - : int list = Pour connaître la longueur d'une liste sans utiliser la fonction du module List définie à cet effet, on peut écrire : (
- Longueur d'une liste
-)
-let rec longueur = function | -> 0 | t :: q -> 1 + longueur q ;; Lors de l'analyse de cette fonction par l'algorithme de synthèse de type, il apparaît que la liste peut contenir n'importe quel type de données, de sorte que la fonction possède le type suivant : val longueur : 'a list -> int =

Fonctions d'ordre supérieur

Les fonctions d'ordre supérieur sont des fonctions qui prennent une ou plusieurs fonctions en entrée et/ou renvoient une fonction. La plupart des langages fonctionnels possèdent des fonctions d'ordre supérieur. Concernant Caml, on peut en trouver des exemples dans les fonctions prédéfinies des modules Array, List, etc.. Par exemple, l'expression suivante :
- List.map (fun i -> i
- i) ;; produira le résultat suivant : - : int list = La fonction map prend en argument la fonction anonyme qui, à tout entier i, associe son carré, et l'applique aux éléments de la liste, construisant ainsi la liste des valeurs élevées au carré.

Arbres et types récursifs

Pour définir un arbre binaire de type quelconque, on se sert d'un type récursif. On peut ainsi avoir recours à l'écriture suivante : type 'a arbre = Feuille | Branche of ('a arbre
- 'a
- 'a arbre) ;; Cet arbre se compose de branches qui se ramifient à souhait, et se terminent par des feuilles. Pour connaître la hauteur d'un arbre, on utilise alors : let rec hauteur = function | Feuille -> 0 | Branche(branche, _, branche') -> 1 + max (hauteur branche) (hauteur branche') ;;

Recherche de racine par dichotomie

let rec dicho f min max eps = let fmin = f min and fmax = f max in if fmin
-. fmax > 0. then failwith "Aucune racine" else if max -. min < eps then (min, max) (
- retourne un intervalle
-) else let mil = (min +. max) /. 2. in if (f mil)
-. fmin < 0. then dicho f min mil eps else dicho f mil max eps ;; (
- Approximation de la racine carrée de 2
-)
- dicho (fun x -> x
-. x -. 2.) 0. 10. 0.000000001;; - : float
- float = (1.4142135618, 1.41421356238)

Critiques

OCaml souffre quelque peu de son manque de popularité. La communauté n'est pas extrêmement active, ce qui peut gêner quand on cherche par exemple un binding en OCaml d'une bibliothèque codée dans un autre langage : on la trouvera moins souvent que dans d'autres langages populaires comme Python, ou elle risque d'être peu (voire pas du tout maintenue). La discrétion de la communauté n'est pas qu'un problème de taille, car on peut observer que la communauté Haskell est beaucoup plus visible, bien que le langage ne soit pas tellement plus utilisé dans l'ensemble. On reproche souvent à OCaml l'absence de polymorphisme ad-hoc, en particulier au niveau de la surcharge des opérateurs : on n'apprécie pas de devoir utiliser deux opérateurs différents, (+) et (+.), pour devoir additionner des entiers ou des flottants. Enfin, en comparaison avec les autres langages fonctionnels répandus comme SML ou Haskell, OCaml souffre de l'absence de multiples implémentations : la seule implémentation complète à ce jour est celle maintenue par l'équipe de l'INRIA, qui est peut-être un peu fermée quant aux contributions externes (il semble par exemple qu'il soit relativement difficile de proposer des améliorations de la bibliothèque standard, comme essaie de le faire le projet ). Il existe depuis peu un projet visant à compiler OCaml vers la machine Java, , qui semble supporter l'ensemble du langage, mais il est encore à un stage précoce du développement, et est une extension du compilateur OCaml officiel, plutôt qu'une implémentation complètement différente.

Langages dérivés

De nombreux langages étendent OCaml pour implémenter par dessus des fonctionnalités un peu plus exotiques.
-F
- est un langage de la plateforme .NET développé par Microsoft Research, basé sur OCaml (et en partie compatible)
- ajoute un mécanisme de quotations et de génération de code au runtime, qui apporte des fonctionnalités de métaprogrammation à OCaml
- (basé sur AlphaCaml, un autre dérivé de OCaml) facilite la manipulation de noms symboliques
- ajoute à OCaml un support du Join Calculus, orienté vers les programmes concurrents ou distribués
- apporte une forme particulière de parallèlisme, basée sur les "squelettes" (skeleton programming)
- ajoute le polymorphisme ad-hoc à OCaml, permettant la surcharge des opérateurs ou un marshalling conservant les informations de typage
- permet au système de types de représenter des valeurs XML ou liées à des expressions régulières. C'est un intermédiaire entre OCaml et le langage CDuce, spécialisé dans la manipulation du XML

Voir aussi

==
#
Accident de Beaune   Amélie Mauresmo   Anisocytose   C3H6O   CA Paris   Carole Richert   Catherinettes   Chaleur massique   Championnat de Tunisie de football D2   Classement mondial des entreprises leader par secteur   Col du Bonhomme (Vosges)   De viris illustribus (Lhomond)   Dolcett   EGP  
^