☀️ Stage Pré-rentrée · dès le 24 aoûtRéserver ma place →
Majorant
📘 Fiche de cours · 1re année💻 MP2I💻 Informatique MP2I / MPINiveau · MP2I

OCaml — Premiers pas

Découvrir OCaml, le langage fonctionnel de MP2I : le let et le typage inféré, les fonctions, le piège des opérateurs pointés (+. pour les float), le if/then/else qui renvoie une valeur, et la récursivité let rec (factorielle déroulée) — avec trois exercices corrigés.

Fiche rédigée par les mentors Majorant — alumni Polytechnique, CentraleSupélec et Mines Paris.

3 définitionsMis à jour le 2026-08-02

Vue d'ensemble

En MP2I tu apprends deux langages complémentaires : le C, impératif, où l'on manipule la mémoire et où l'on modifie des variables case par case ; et OCaml, fonctionnel, où l'on calcule en composant des fonctions et où l'on ne modifie (presque) rien. Cette fiche est ta première rencontre avec OCaml : les valeurs, les fonctions, les types, le conditionnel et la récursivité. L'objectif n'est pas de te faire réciter une syntaxe, mais de te faire comprendre la logique de ce langage — car elle est très différente de celle du C.

Au programme. Liaison let et inférence de type ; définition et application de fonctions ; types de base int, float (opérateurs pointés), bool, char, string ; l'expression conditionnelle if … then … else … ; la récursivité avec let rec ; l'immutabilité des liaisons. Point de vigilance permanent : le typage fort qui interdit de mélanger int et float.

Prérequis

  • Aucun prérequis OCaml : on part de zéro.
  • Savoir ce qu'est une fonction au sens mathématique () aide énormément.
  • Notion de récursivité (une suite définie par récurrence, comme ).
  • Un contraste avec le C est le bienvenu : garde en tête « en C je ferais… » à chaque étape.
🎯 Accompagnement Majorant

Le fonctionnel te déroute au début, c'est normal. Le passage de la pensée « je modifie des cases mémoire » à « je calcule des valeurs » est le vrai obstacle de l'année en info. Nos mentors alumni X · Centrale · Mines l'ont franchi et te montrent les bons réflexes dès le premier TP.

Trouver un mentor →

Valeurs et liaisons : le mot-clé let

En OCaml, la brique de base est la liaison : on donne un nom à une valeur avec let. On ne déclare jamais le type : le compilateur le devine tout seul (on parle d'inférence de type).

let x = 3
let message = "bonjour"
let pi = 3.14159
🔍 Décryptage ligne par ligne
let x = 3on lie le nom x à la valeur 3. Le compilateur infère le type int car 3 est un entier. Aucun int x à écrire, contrairement au C.
let message = "bonjour"ici message a le type string (chaîne de caractères, entre guillemets doubles), toujours inféré.
let pi = 3.14159la présence d'un point décimal fait inférer le type float. Le type est donc déterminé par la forme de la valeur.
Définition 1.1 — Liaison let

Une liaison let nom = expression évalue d'abord expression, obtient une valeur, puis attache définitivement le nom nom à cette valeur. Le type de nom est celui de la valeur, inféré automatiquement.

⚠ « let » n'est pas une affectation. En C, x = 3; range 3 dans une case que l'on pourra plus tard écraser. En OCaml, let x = 3 ne crée pas de case modifiable : c'est une définition. On y revient dans la section sur l'immutabilité.

Les fonctions

Une fonction se définit elle aussi avec let : on écrit le nom, puis les paramètres, puis =, puis le corps. Ici encore, aucun type n'est écrit : tout est inféré.

let carre x = x * x

let somme a b = a + b
🔍 Décryptage ligne par ligne
let carre x = x * xdéfinit une fonction carre à un paramètre x. Le corps x * x utilise * (multiplication entière), donc OCaml infère que x est un int et que carre a le type int -> int (« prend un int, rend un int »).
let somme a b = a + bfonction à deux paramètres, écrits l'un après l'autre séparés par une espace (pas de virgule, pas de parenthèses). Type inféré : int -> int -> int.

Pour appliquer une fonction, on écrit son nom suivi de ses arguments séparés par des espaces. Les parenthèses ne sont pas obligatoires autour d'un argument simple.

let r = carre 7
let s = somme 4 10
🔍 Décryptage ligne par ligne
let r = carre 7on applique carre à l'argument 7. On écrit carre 7, jamais carre(7) obligatoirement. Résultat : r = 49.
let s = somme 4 10on applique somme à deux arguments. En C on écrirait somme(4, 10) ; en OCaml c'est somme 4 10. Résultat : s = 14.
⚠ Les parenthèses regroupent, elles n'appellent pas. On met des parenthèses uniquement pour délimiter un argument composé : carre (3 + 1) calcule carre 4 = 16. Sans parenthèses, carre 3 + 1 se lit (carre 3) + 1 = 10 : l'application est plus prioritaire que le +.
Définition 1.2 — Fonction et application

Une fonction se définit par let nom p1 p2 … = corps. Son application s'écrit nom arg1 arg2 … : les arguments sont juxtaposés, séparés par des espaces. L'application lie chaque paramètre à l'argument correspondant, puis évalue le corps.

Les types de base

OCaml est à typage fort et statique : chaque valeur a un type fixé, et le compilateur refuse tout mélange incohérent. Voici les types que tu rencontres dès le premier jour.

int et float : deux mondes séparés

Le type int représente les entiers. Ses opérateurs sont +, -, *, / et mod. Attention : / sur des int est la division entière (le quotient), et mod donne le reste.

let q = 7 / 2
let r = 7 mod 2
🔍 Décryptage ligne par ligne
let q = 7 / 2/ entre deux int est la division entière : elle tronque. 7 / 2 vaut 3, pas 3.5. Exactement comme / entre int en C.
let r = 7 mod 2mod donne le reste de la division entière : 7 mod 2 = 1. C'est l'équivalent du % du C.

Le type float représente les nombres à virgule. Ses opérateurs sont pointés : +., -., *., /.. C'est LE piège central du débutant.

let a = 3.0 +. 2.0
let b = 10.0 /. 4.0
🔍 Décryptage ligne par ligne
let a = 3.0 +. 2.0pour additionner deux float, on écrit +. (le point collé au +). 3.0 +. 2.0 vaut 5.0. Écrire 3.0 + 2.0 est une erreur de type : + attend des int.
let b = 10.0 /. 4.0/. est la division flottante : 10.0 /. 4.0 vaut 2.5. À comparer avec 10 / 4 qui vaut 2 (division entière).
⚠ On ne mélange JAMAIS int et float. 3 + 2.0 ne compile pas : OCaml n'insère aucune conversion automatique (pas de « promotion » comme en C). Pour passer d'un monde à l'autre il faut convertir explicitement avec float_of_int ou int_of_float. Retiens : + pour les int, +. pour les float, et les deux camps ne se rencontrent pas.

bool, char et string

Trois autres types de base à connaître :

  • bool : les deux valeurs true et false. Comparaisons : = (égalité), <, >, <=, >=, <> (différent). Connecteurs : && (et), || (ou), not.
  • char : un caractère unique entre apostrophes, comme 'A' ou 'z'.
  • string : une chaîne entre guillemets doubles, comme "info". On concatène deux chaînes avec l'opérateur ^.
let salut = "bon" ^ "jour"
let test = (3 < 5) && (2 <> 2)
🔍 Décryptage ligne par ligne
let salut = "bon" ^ "jour"^ est la concaténation de chaînes : "bon" ^ "jour" vaut "bonjour". Type inféré : string.
let test = (3 < 5) && (2 <> 2)3 < 5 vaut true ; 2 <> 2 (« 2 différent de 2 ») vaut false ; true && false vaut false. Type inféré : bool.
📝 Le tableau à mémoriser. int+ - * / mod ; float+. -. *. /. ; string^. Une grande partie des erreurs de compilation du débutant vient d'un opérateur pris dans le mauvais monde.

Le conditionnel : if … then … else …

En OCaml, if condition then A else B n'est pas une instruction : c'est une expression qui renvoie une valeur. Elle vaut A si la condition est true, sinon B. On peut donc la lier à un nom ou la passer à une fonction.

let maximum = if 5 > 3 then 5 else 3

let signe x = if x >= 0 then "positif" else "negatif"
🔍 Décryptage ligne par ligne
let maximum = if 5 > 3 then 5 else 3la condition 5 > 3 vaut true, donc l'expression tout entière vaut la branche then, soit 5. On lie ce résultat à maximum. Le if a produit une valeur, pas une action.
let signe x = if x >= 0 then "positif" else "negatif"fonction qui renvoie une string. Les deux branches doivent avoir le même type (ici string), sinon erreur de type.
⚠ Le else est presque toujours obligatoire. Puisque le if doit produire une valeur, il faut dire quoi renvoyer dans les deux cas. Un if … then A sans else n'est autorisé que si A est de type unit (une action sans résultat utile), cas rare en début d'année. En pratique : mets toujours un else.
⚠ Deux sens du symbole =. Dans let x = 3, le = est une liaison (« on définit »). Dans if x = 0 then …, le = est le test d'égalité (« est-ce que x vaut 0 ? »), qui renvoie un bool. C'est le contexte qui tranche. Attention : contrairement au C, l'égalité s'écrit avec un seul =, pas ==.

La récursivité : let rec

OCaml n'a pas de boucle for ou while au cœur de sa logique : pour répéter un calcul, on écrit une fonction qui s'appelle elle-même. Mais une fonction ordinaire définie par let ne connaît pas son propre nom. Pour qu'elle puisse se rappeler, il faut le mot-clé rec.

Définition 2.1 — Fonction récursive (let rec)

Une fonction récursive est une fonction dont le corps contient un appel à elle-même. En OCaml, on la définit avec let rec nom … = …. Le mot-clé rec (pour récursif) rend le nom de la fonction visible à l'intérieur de son propre corps. Sans rec, ce nom serait « inconnu » et la compilation échouerait.

L'exemple canonique est la factorielle : , avec la convention . La définition par récurrence se traduit presque mot pour mot :

let rec fact n =
  if n = 0 then 1
  else n * fact (n - 1)
🔍 Décryptage ligne par ligne
let rec fact n =on définit une fonction récursive fact d'un paramètre n. Le rec est indispensable : sans lui, le fact de la dernière ligne serait inconnu.
if n = 0 then 1le cas de base, qui arrête la récursion. Ici n = 0 est un test d'égalité (renvoie un bool) ; si n vaut 0, on renvoie 1 (car ) sans se rappeler.
else n * fact (n - 1)le cas récursif : on renvoie n multiplié par le résultat de fact (n - 1). Les parenthèses autour de n - 1 sont nécessaires, sinon OCaml lirait (fact n) - 1. Chaque appel diminue n de 1, donc on finit par atteindre le cas de base.

Déroulons l'appel fact 4. À chaque étape, OCaml suspend le calcul en cours (il ne connaît pas encore fact 3) et lance un nouvel appel, jusqu'au cas de base ; puis il « remonte » en multipliant.

Déroulé de fact 4 — descente jusqu'au cas de base, puis remontée
ÉtapeAppel évaluénCe que renvoie l'appel
1 (descente)fact 444 * fact 3 — en attente
2 (descente)fact 333 * fact 2 — en attente
3 (descente)fact 222 * fact 1 — en attente
4 (descente)fact 111 * fact 0 — en attente
5 (cas de base)fact 001 — la récursion s'arrête
6 (remontée)fact 111 * 1 = 1
7 (remontée)fact 222 * 1 = 2
8 (remontée)fact 333 * 2 = 6
9 (remontée)fact 444 * 6 = 24

On lit donc fact 4 . La descente empile les multiplications en attente ; la remontée les effectue dans l'ordre inverse.

📐 Méthode — Écrire une fonction récursive sûre
  1. Écris let rec (jamais let seul pour une fonction qui s'appelle).
  2. Traite d'abord le cas de base avec un if : la plus petite entrée pour laquelle la réponse est immédiate (ici n = 0).
  3. Dans le cas récursif, appelle la fonction sur une entrée strictement plus proche du cas de base (ici n - 1) — sinon la récursion ne s'arrête jamais.
  4. Combine le résultat de l'appel récursif avec la valeur courante (ici la multiplication par n).
⚠ Oublier le rec ou le cas de base. Sans rec, le compilateur affiche Unbound value fact. Sans cas de base (ou avec un argument qui ne se rapproche pas de 0), la fonction s'appelle indéfiniment : c'est le débordement de pile (Stack overflow). Toute récursion a besoin d'une porte de sortie.
🎯 Accompagnement Majorant

La récursivité, c'est le socle de tout le semestre. Tri fusion, arbres, backtracking : tout repose sur le réflexe cas de base / cas récursif vu ici. Nos mentors alumni X · Centrale · Mines t'entraînent à le poser proprement pour qu'il devienne automatique avant les premiers DS.

Trouver un mentor →

L'immutabilité : une liaison ne change pas

Voici la différence de mentalité la plus importante avec le C. En C, une variable est une case mémoire que l'on écrase autant qu'on veut : x = x + 1; a un sens. En OCaml, une liaison let attache un nom à une valeur, définitivement : il n'existe pas d'affectation destructive de ce genre.

let x = 3
let x = x + 1
🔍 Décryptage ligne par ligne
let x = 3on crée une liaison : le nom x désigne la valeur 3. Cette valeur ne changera jamais.
let x = x + 1ce n'est PAS une modification de la case x. On crée une nouvelle liaison, également nommée x, qui vaut l'ancien x (3) plus 1, soit 4. L'ancienne liaison existe toujours « en dessous » ; on l'a simplement masquée. On appelle cela le shadowing.
📝 Masquage, pas mutation. Le résultat visible (« x vaut maintenant 4 ») ressemble à une modification, mais le mécanisme est tout autre : aucune valeur n'a été écrasée en mémoire, on a juste créé une seconde définition qui cache la première. Cette distinction devient cruciale avec les fonctions et les portées.
💡 Pourquoi c'est un avantage. Comme rien ne change « dans le dos » du programmeur, une fonction appelée deux fois sur les mêmes arguments donne toujours le même résultat. On peut raisonner sur le code comme sur des formules mathématiques : carre 5 vaut 25, hier, aujourd'hui et partout. C'est ce qui rend les programmes fonctionnels plus faciles à prouver corrects.

Exercices corrigés

Exo 1Types et opérateursFacile

Pour chacune des expressions suivantes, donne sa valeur ou indique si elle provoque une erreur de type, en justifiant : (a) 15 / 4 ; (b) 15 mod 4 ; (c) 2.5 +. 1.5 ; (d) 2 +. 3 ; (e) "ab" ^ "cd".

Voir la correction détaillée
(a) 15 / 4 : / entre deux int est la division entière. On cherche le quotient de 15 par 4 : , donc la valeur est 3 (le reste 3 est ignoré).
(b) 15 mod 4 : mod donne le reste. D'après , la valeur est 3.
(c) 2.5 +. 1.5 : les deux opérandes sont des float et l'opérateur pointé +. convient. Valeur : 4.0.
(d) 2 +. 3 : erreur de type. L'opérateur +. attend deux float, or 2 et 3 sont des int. OCaml n'insère aucune conversion. Il aurait fallu 2 + 3 (valeur 5) ou 2.0 +. 3.0 (valeur 5.0).
(e) "ab" ^ "cd" : ^ concatène deux string. Valeur : "abcd".
Exo 2Somme des entiers de 0 à nIntermédiaire

Écris une fonction récursive somme telle que somme n vaut . Précise le cas de base et le cas récursif, puis déroule somme 3.

Voir la correction détaillée
Idée. On a la relation de récurrence , avec le cas de base . On la traduit directement avec let rec.
Code.
let rec somme n =
  if n = 0 then 0
  else n + somme (n - 1)
Décryptage. let rec somme n = déclare la fonction récursive. if n = 0 then 0 est le cas de base (arrêt, on renvoie 0). else n + somme (n - 1) ajoute n au résultat de l'appel sur n - 1, plus proche du cas de base.
Déroulé de somme 3. somme 3 = 3 + somme 2 = 3 + (2 + somme 1) = 3 + (2 + (1 + somme 0)) = 3 + (2 + (1 + 0)). En remontant : somme 0 = 0, somme 1 = 1, somme 2 = 3, somme 3 = 6. Résultat : 6.
Exo 3Puissance récursiveDifficile

Écris une fonction récursive puissance telle que puissance x n vaut (pour x et n entiers, n >= 0), en utilisant et . Déroule puissance 2 4, et explique pourquoi la récursion doit porter sur n et non sur x.

Voir la correction détaillée
Code.
let rec puissance x n =
  if n = 0 then 1
  else x * puissance x (n - 1)
Décryptage. let rec puissance x n = : deux paramètres, x (la base) et n (l'exposant). if n = 0 then 1 : cas de base, car . else x * puissance x (n - 1) : on multiplie par x le résultat de l'appel où l'exposant a diminué de 1. La base x est recopiée inchangée à chaque appel.
Déroulé de puissance 2 4. 2 * puissance 2 3 = 2 * (2 * puissance 2 2) = 2 * (2 * (2 * puissance 2 1)) = 2 * (2 * (2 * (2 * puissance 2 0))). Le cas de base donne puissance 2 0 = 1. En remontant : , , , . Résultat : 16.
Pourquoi décroître n. Le cas de base porte sur l'exposant (n = 0). Pour garantir l'arrêt, chaque appel doit rapprocher un argument de ce cas de base : c'est n qui doit décroître vers 0. Faire décroître x ne rapprocherait d'aucun cas de base et changerait le calcul.

Récap final — Ce qu'il faut absolument retenir

OCaml est un langage fonctionnel à typage statique inféré. On y calcule des valeurs plutôt que de modifier des cases. Vérifie que tu maîtrises chacun de ces réflexes.

  • Sais-tu lier une valeur avec let x = … sans jamais écrire son type (inférence) ?
  • Sais-tu définir let f x = … et l'appliquer par f 7, arguments juxtaposés sans virgule ?
  • Sais-tu que + - * / mod sont pour les int et +. -. *. /. pour les float, et qu'on ne mélange jamais les deux ?
  • Sais-tu que / entre int est la division entière (7 / 2 = 3) et que ^ concatène les chaînes ?
  • Sais-tu que if … then … else … est une expression qui renvoie une valeur, avec les deux branches de même type ?
  • Sais-tu distinguer le = de liaison du = de test d'égalité selon le contexte ?
  • Sais-tu qu'une fonction récursive exige let rec et pose toujours un cas de base plus un cas récursif qui s'en rapproche ?
  • Sais-tu dérouler fact 4 (descente puis remontée) et retrouver 24 ?
  • Sais-tu qu'une liaison let est immuable et que let x = x + 1 masque l'ancien x au lieu de le modifier ?

Débloque la fiche complète

Théorèmes, démonstrations à savoir refaire, méthodes-types et pièges de concours : crée ton compte gratuit pour tout lire. Une seule fois pour toutes les fiches et ressources Majorant.

Gratuit · vos données restent confidentielles.

Valide tes acquis

Quiz — OCaml : premiers pas

11 questions · une à la fois · seuil de maîtrise 80 %.

MP2I / MPI · MP2IQuiz — OCaml — Premiers pasQuestion 1 / 11
FacileChoix unique1 pt

En OCaml, quand on écrit une liaison comme let vitesse = 90, comment le type de vitesse est-il déterminé ?

Sélectionne une réponse pour valider.

Fiches associées

💻 MP2I·Informatique

C — Premiers pas

Écrire son premier programme C : la structure main/return, les types de base, printf et ses formats, les boucles for/while, et le piège n°1 — la division entière (7/2 vaut 3, pas 3,5) — chaque programme compilé et tracé, avec trois exercices corrigés.

💻 MP2I·Informatique

C — Pointeurs et allocation dynamique

Le cœur du C : l'adresse et le pointeur, les opérateurs & et *, pourquoi il faut un pointeur pour modifier une variable (le passage par valeur), le lien tableaux/pointeurs, et malloc/free — chaque programme compilé et tracé, avec trois exercices corrigés.

💻 MP2I·Informatique

OCaml — Filtrage et listes

Les deux piliers d'OCaml : le filtrage (match ... with) et les listes récursives (:: et []), avec longueur et somme déroulées sur un exemple, les types somme et le type option (Some/None) — attention à l'ordre et à l'exhaustivité des cas, avec trois exercices corrigés.

💻 MP2I·Informatique

C — Structures et listes chaînées

La première structure de données dynamique du programme : les struct, le maillon et l'opérateur flèche p->suivant, l'insertion en tête et le parcours d'une liste chaînée — construction de [3, 5, 8] tracée, avec les pièges (NULL, ordre inversé, fuite mémoire) et trois exercices corrigés.

💻 MP2I·Informatique

C — Piles et files

Les deux structures linéaires fondamentales implémentées en C : la pile (LIFO, empiler/dépiler en tête) et la file (FIFO, avec un pointeur de queue pour enfiler en O(1)) — chaque opération compilée et tracée, avec trois exercices corrigés.

💻 MP2I·Informatique

OCaml — Arbres binaires

L'arbre binaire comme type somme récursif OCaml (Vide | Noeud) : taille, hauteur et parcours infixe écrits par filtrage sur Vide / Noeud(g,x,d), déroulés à la main sur un petit arbre — chaque cas du type devient un cas du match, avec trois exercices corrigés.

Tu veux aller plus loin sur ce chapitre ?

Nos mentors alumni de Polytechnique, CentraleSupélec et Mines Paris t'accompagnent en cours particuliers — démonstrations détaillées, exos type concours, oraux blancs.

Trouver un mentor →