Introduction
Table des matières

OCaml est un langage qui se place dans l'histoire des langages de programmation comme un lointain descendant de LISP ayant su tirer les enseignements de ses cousins en incorporant les principales caractéristiques des autres langages. Il est développé à l'INRIA et s'appuie sur une longue expérience de conception de langages de la famille ML. OCaml est généraliste pour l'expression d'algorithmes symboliques ou numériques. Il est orienté objet et possède un système de modules paramétrés. Il permet le développement d'applications concurrentes ou distribuées. Il possède une excellente sûreté à l'exécution grâce à son typage statique, son mécanisme d'exceptions et son récupérateur automatique de mémoire. Il est performant tout en étant portable. Enfin, il dispose d'un riche environnement de développement.

OCaml est développé et maintenu principalement par des chercheurs et ingénieurs de l'INRIA (équipe-projet Gallium, SED de Rocquencourt, Pierre Weis de l'équipe-projet Estime), des chercheurs extérieurs comme Jacques Garrigue (Université de Nagoya, au Japon) ou encore des entreprises membres du Consortium OCaml (OCamlpro, Lexifi, Jane Street, ...).

1. Description du langage

Nous listons ici les traits principaux d'OCaml. Tous ces aspects ne sont pas abordés dans cette formation. Ceux qui le sont figurent en gras.

  • OCaml est un langage fonctionnel: il manipule les fonctions comme étant des valeurs du langage. Celles-ci peuvent être utilisées en tant que paramètres d'autres fonctions ou être retournées comme résultat d'un appel de fonction.
  • OCaml est typé statiquement: la vérification de la compatibilité entre les types des paramètres formels et des paramètres d'appel est effectuée au moment de la compilation du programme. Dès lors, il n'est pas nécessaire de faire ces vérifications durant l'exécution du programme ce qui accroît son efficacité. En outre, la vérification de type permet d'éliminer la plupart des erreurs introduites par maladresse ou étourderie et contribue à la sûreté de l'exécution.
  • OCaml est polymorphe paramétrique: une fonction qui n'explore pas la totalité de la structure d'un de ses arguments accepte que celui-ci ait un type non entièrement déterminé. Ce paramètre est alors dit polymorphe. Cette particularité permet de développer un code générique utilisable pour des structures de données différentes tant que la représentation exacte de cette structure n'a pas besoin d'être connue par le code en question. L'algorithme de typage est à même de faire cette distinction.
  • OCaml possède une inférence de types: le programmeur n'a besoin de donner aucune information de type à l'intérieur de son programme. Le langage se charge seul de déduire du code le type le plus général des expressions et des déclarations qui y figurent. Cette inférence est effectuée conjointement à la vérification, lors de la compilation du programme.
  • OCaml est muni d'un mécanisme d'exceptions: il est possible de rompre l'exécution normale d'un programme à un endroit et de la reprendre à un autre endroit du programme prévu à cet effet. Ce mécanisme permet de gérer les situations exceptionnelles, mais il peut aussi être adopté comme style de programmation.
  • OCaml possède des traits impératifs: les entrées-sorties, les modifications physiques de valeurs et les structures de contrôle itératives sont possibles sans avoir recours aux traits de la programmation fonctionnelle. Le mélange des deux styles est bien entendu accepté et offre une grande souplesse de développement ainsi que la possibilité de définir des structures de données nouvelles.
  • OCaml exécute des processus légers (threads): les principaux outils de création, de synchronisation pour la gestion de la mémoire partagée et de communication entre différents processus légers sont prédéfinis.
  • OCaml communique sur le réseau Internet: les bases nécessaires à l'ouverture de canaux de communication entre différentes machines sont prédéfinies et permettent la réalisation d'applications suivant l'architecture client-serveur.
  • OCaml dispose de nombreuses bibliothèques: structures de données classiques, entrées-sorties, interfaçage avec les ressources du système, analyses lexicale et syntaxique, calcul sur les grands nombres, valeurs persistantes, etc.
  • OCaml dispose d'un environnement de programmation: incluant une boucle d'interaction, une trace de l'exécution, un calcul des dépendances et une analyse de performance.
  • OCaml interagit avec le langage C: via l'appel de fonctions C à partir d'un programme OCaml et réciproquement, permettant ainsi l'accès aux nombreuses bibliothèques C.
  • OCaml dispose de trois modes d'exécution: interactif par le biais d'une boucle d'interaction, compilation vers du code-octet interprété par une machine virtuelle, compilation vers du code machine. Le programmeur peut donc choisir entre souplesse de développement, portabilité du code objet sur différentes architectures ou performance sur une architecture donnée.
  • OCaml dispose d'un garbage collector ("GC"), aussi appelé "glaneur de cellules" ou "ramasse-miettes": il n'y a aucune allocation explicite de mémoire, le GC s'occupant d'allouer automatiquement l'espace mémoire nécessaire au stockage des structures de données manipulées et de le libérer automatiquement lorsque ces données ne sont plus accessibles.
2. Evaluation, compilation, exécution

Il existe plusieurs façons d'exécuter un programme OCaml:

2.1. Utilisation de l'interprète (toplevel)

Il est possible d'interpréter le code d'un programme, de façon interactive ou non, grâce au toplevel. Le toplevel OCaml permet de saisir et exécuter interactivement du code OCaml, de la même façon que dans un interprète de commandes UNIX (shell). L'inférence et la vérification des types sont faites et le code exécuté immédiatement. Ce mode permet de tester facilement de petits bouts de programme, sans avoir à compiler puis exécuter toute une application.

Le toplevel est lancé par la commande ocaml. L'utilisateur peut alors saisir des phrases OCaml terminées par ";;" qui sont évaluées immédiatement. Le toplevel affiche les types et valeurs des expressions évaluées avant de réafficher le prompt:

# "Bonjour le monde !" (* code saisi par l'utilisateur *);;
- : string = "Bonjour le monde !"
# 42.0;;
- : float = 42.

Il est possible de charger des modules additionnels compilés (cf. chapitre Programmation modulaire) soit en donnant ces modules sur la ligne de commande:

$ ocaml mon_module.cmo mon_module2.cmo

soit en utilisant des directives de chargement #load à l'invite du toplevel:

# #load "mon_module.cmo";;
Cannot find file mon_module.cmo.

# #load "mon_module2.cmo";;
Cannot find file mon_module2.cmo.

Le contenu des modules chargés est alors accessible depuis le code saisi dans le toplevel.

Il est également possible de lancer le toplevel sur un fichier de code OCaml, ce dernier sera évalué comme si son contenu était saisi à l'invite du toplevel:

ocaml mon_module.ml

Pour faciliter la saisie de code (historique, utilisation des flèches du clavier, ...) et l'évaluation dans le toplevel, on pourra utiliser un toplevel amélioré, Utop, installable via Opam.

Il existe également un éditeur graphique dédié à l'apprentissage d'OCaml: OCaml-top.

Un environnement en ligne d'apprentissage d'OCaml, avec des exercices, est également disponible: Learn-OCaml.

Pour la suite de la formation, nous utiliserons un éditeur et ferons exécuter notre code par la commande

$ ocaml notre_fichier.ml

Pour en savoir plus sur le toplevel, consulter le manuel ici.

2.2. Compilation vers du code-octet

Il existe deux compilateurs OCaml différents, l'un compilant vers du code-octet (byte-code), l'autre vers du code natif.

Le code-octet peut être exécuté par une machine virtuelle OCaml sur d'autres architectures que celle pour laquelle il a été compilé. C'est le même principe que le code-octet pour machine virtuelle Java. Des performances moindres sont la contrepartie de cette portabilité. Par ailleurs, dans le cas d'utilisation de bibliothèques C, ces bibliothèques doivent être présentes sur la machine d'exécution.

Les principales extensions des fichiers utilisées par les compilateurs OCaml et leurs significations sont indiquées dans le tableau de la Figure 1.

Figure 1: Principales extensions des noms de fichiers utilisées par les compilateurs OCaml.
.ml Source d'implémentation (équivalent des .c en C)
.mli Source d'interface (cf. chapitre Programmation modulaire, sorte d'équivalent des .h en C)
.cmo Implémentation compilée en code-octet
.cmi Interface compilée
.cmx et .o Implémentation compilée en code natif
.cma Bibliothèque (regroupement de plusieurs implémentations compilées) en code-octet
.cmxa et .a Bibliothèque en code natif
.cmxs Bibliothèque en code natif compilée pour le chargement dynamique.

Le compilateur code-octet est ocamlc, invoqué par des commandes de la forme suivante:

ocamlc [options] fichiers

Les options les plus courantes sont les suivantes:

  • -c: indique au compilateur de simplement compiler les fichiers sources en paramètres, sans faire l'édition de liens pour créer un exécutable. Ainsi, la commande
    ocamlc -c foo.ml
    produira un fichier .cmo, le code-octet correspondant au code de foo.ml. Le fichier .cmo pour un fichier .ml est donc l'équivalent des fichiers objets .o pour les fichiers de code C .c. Contrairement au langage C pour lequel les fichiers d'interface .h ne sont pas compilés, il est nécessaire en OCaml de compiler le code des interfaces:
    ocamlc -c foo.mli
    compilera donc l'interface foo.mlidans un fichier foo.cmi. Dans le cas où un fichier .mli existe pour un fichier .ml, il est nécessaire de compiler d'abord l'interface pour créer le .cmi. La compilation du .ml en .cmo vérifiera que l'implémentation (.ml) correspond bien à l'interface. S'il n'existe pas de fichier d'interface .mli pour un fichier d'implémentation .ml, la compilation du .ml créera le .cmo ainsi que l'interface compilée .cmi. L'interface ainsi créée exposera tous les éléments de l'implémentation.
  • -o: indique le nom du fichier à créer, notamment lors de la création d'exécutables. Ainsi, la commande suivante crée l'exécutable program à partir des trois fichiers de code-octet en paramètres:
    ocamlc -o program foo.cmo bar.cmo gee.cmo
    Les fichiers .cmo doivent être indiqués par ordre de dépendance, avec en premier les modules ne dépendant d'aucun autre.
  • -I: indique un répertoire supplémentaire dans lequel chercher les fichiers nécessaires à la compilation (interfaces et implémentations des modules et bibliothèques utilisés). C'est l'équivalent de la même option pour la compilation de fichiers C. Ces indications sont également appelées includes. Par défaut, les répertoires utilisés sont le répertoire courant et le répertoire standard des bibliothèques OCaml, indiqué par la commande
    ocamlc -where
    Les répertoires indiqués par -I sont ajoutés en tête de la liste des répertoires à examiner, mais dans l'ordre dans lequel ils sont indiqués. Ainsi, la commande
    ocamlc -I rep1 -I rep2 ...
    indiquera au compilateur de chercher les fichiers .cmo, .cmi et .cma dans les répertoires rep1, rep2, . et le répertoire standard des bibliothèques OCaml, dans cet ordre. La recherche de tels fichiers n'est pas faite récursivement dans les répertoires inclus. Ainsi, si le répertoire standard des bibliothèques OCaml est /opt/ocaml/lib/ocaml et que l'on souhaite utiliser la bibliothèque Labltk, on devra spécifier ce répertoire à inclure:
    ocamlc -I /opt/ocaml/lib/ocaml/labltk ...
    Une notation abrégée existe pour ajouter un répertoire se trouvant sous le répertoire standard; ainsi l'option -I ci-dessous a le même effet que celle de la commande précédente:
    ocamlc -I +labltk ...
    Cette notation permet de s'abstraire du répertoire d'installation des bibliothèques standard.

Beaucoup d'autres options de compilation existent. Nous ne présentons ici que les options de base permettant de compiler des programmes simples. On se réfèrera donc au manuel pour connaître toutes les options et possibilités du compilateur.

2.3. Compilation vers du code natif

La compilation vers du code natif permet d'obtenir de bien meilleures performances pour l'exécutable produit. Cependant, l'exécutable obtenu ne peut pas être exécuté sur d'autres architectures que celle sur laquelle il a été compilé.

Le compilateur vers du code natif est ocamlopt. Il s'utilise de la même façon que le compilateur ocamlc (cf. cette section), mais utilise des extensions différentes pour les fichiers utilisés et produits (cf. Figure 1).

On peut consulter la documentation relative au compilateur vers du code natif.

3. Le site web

Le site https://ocaml.org/ est le site officiel d'OCaml et on y trouve, en plus des distributions, tout le matériel nécessaire pour débuter en OCaml: documentations, références de livres, tutoriels, liens, ...

4. Accès aux documentations

Les documentations de base pour développer en OCaml sont les suivantes:

  • Le manuel de référence,
  • Les pages man: chaque outil (ocamlc, ocamlopt, ocamldoc, ...) et chaque module de la bibliothèque standard possède sa propre page de manuel. Ainsi, on accédera à la page man du module List par la commande man List.