Le langage OCaml
Cours · informatique (MP2I/MPI), chapitre 2 · MP2I et MPI · le début du chapitre, la suite sur Adloun
2.1 Le contraire de C, et c'est voulu
Le chapitre précédent s'achevait sur cinq points où C montre la machine : types déclarés, entiers qui débordent, accès non vérifiés, passage par valeur, mémoire rendue à la main. OCaml prend l'autre chemin sur quatre d'entre eux, et le programme officiel dit pourquoi : OCaml « permet en particulier aux étudiants de recourir rapidement à un niveau d'abstraction supérieur et de manipuler facilement des structures de données récursives ».
Deux langages, donc, et à parité : « on veille à développer de façon parallèle les compétences de programmation dans ces deux langages ». Ce n'est pas de la redondance. Un même algorithme écrit deux fois, dans deux langages qui ne font pas les mêmes choix, sépare ce qui appartient à l'algorithme de ce qui n'appartient qu'à sa mise en œuvre. C'est exactement ce qu'un concours vous demandera de savoir distinguer.
| C | OCaml | |
|---|---|---|
| Types | déclarés par le programmeur | inférés par le compilateur |
| Vérification | statique, mais faible | statique et forte |
| Mémoire | malloc / free | automatique |
| Structure de base | le tableau | la liste et le type somme |
| Style dominant | impératif | fonctionnel, l'impératif restant possible |
Elle porte sur OCaml « version 4 ou supérieure », et distingue là encore ce qui est su sans rappel de ce qui l'est après rappel (section sec:ocaml-apres-rappel). Les modules au sens de module M = struct ... end, les foncteurs, les objets ne sont pas au programme : on utilise des modules (List.length), on n'en écrit pas.
2.2 Tout est expression
En C, if est une instruction : elle fait quelque chose. En OCaml, if est une expression : elle vaut quelque chose.
let m = if a > b then a else b (* m reçoit la VALEUR de l'expression *)
Conséquence immédiate : le else n'est pas facultatif quand la valeur est utilisée — une expression doit valoir quelque chose dans tous les cas. Et les deux branches doivent avoir le même type, car l'expression entière n'en a qu'un.
let x = 3 (* définition globale *)
let y = x + 1 in y * 2 (* définition locale : y n'existe que dans « y * 2 » *)
let carre x = x * x (* une fonction : pas de parenthèses, pas de return *)
let rec fact n = (* rec : la fonction peut s'appeler elle-même *)
if n = 0 then 1 else n * fact (n - 1)
let ... in ... est lui-même une expression : il vaut son second membre.
let ne fait pas d'affectationlet x = 3 ne crée pas une variable modifiable : il lie le nom x à la valeur 3, définitivement. Écrire ensuite let x = 4 ne modifie pas la première liaison, il en crée une seconde qui masque la première. La distinction est visible dès qu'une fonction entre en jeu :
let a = 10
let f y = y + a (* f capture la valeur de a AU MOMENT DE LA DÉFINITION *)
let a = 100
let z = f 1 (* z vaut 11, et non 101 *)
C'est la portée lexicale, que l'annexe nomme explicitement : « lorsqu'une définition utilise une variable globale, c'est la valeur de cette variable au moment de la définition qui est prise en compte ».
2.3 Le typage : inféré, fort, polymorphe
Vous n'écrivez pas les types ; le compilateur les trouve.
let double x = 2 * x
(* le compilateur répond : val double : int -> int = <fun> *)
Il a raisonné ainsi : * est la multiplication des entiers, donc x est un int, donc le résultat aussi. La notation int -> int se lit « d'int vers int ».
Quand rien ne contraint le type, il reste variable :
let identite x = x (* val identite : 'a -> 'a *)
let premier (a, b) = a (* val premier : 'a * 'b -> 'a *)
'a se lit « alpha » et désigne n'importe quel type. Une seule fonction sert alors pour tous les types — c'est le polymorphisme, dont l'annexe demande une « idée naïve ».
1 + 2.5 (* ERREUR de compilation : + attend deux int *)
1.0 +. 2.5 (* correct : les flottants ont LEURS opérateurs, suffixés d'un point *)
Les opérateurs flottants sont +., -., *., /.. C'est déroutant deux jours, puis c'est un filet : en C, 7 / 2 valait sans un mot. En OCaml, une division entière ne peut pas se déguiser en division réelle.