Adloun

Le langage OCaml

Cours · informatique (MP2I/MPI), chapitre 2 · MP2I et MPI · le début du chapitre, la suite sur Adloun

Un programme dans un éditeur de code : des instructions exécutées de haut en bas.

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.

COCaml
Typesdéclarés par le programmeurinférés par le compilateur
Vérificationstatique, mais faiblestatique et forte
Mémoiremalloc / freeautomatique
Structure de basele tableaula liste et le type somme
Style dominantimpératiffonctionnel, l'impératif restant possible
ImportantL'annexe B est limitative, comme l'annexe A

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

Définition 2.1Il n'y a pas d'instruction en OCaml

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.

Définition 2.2Lier une valeur à un nom

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.

Attentionlet ne fait pas d'affectation

let 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

Définition 2.3Inférence

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 ».

Définition 2.4Polymorphisme

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 ».

AttentionLe typage fort ne pardonne aucun mélange

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.

Exercices du chapitre

La suite du cours · Tous les exercices du chapitre