Découverte d'OCAML

Découverte d’OCAML

Dans ce petit article, je compte vous parler d’un langage de programmation assez particulier : (O)CAML. Tout d’abord, si j’ajoute une parenthèse devant la première lettre du sigle, c’est que je ne compte pas aborder toute la partie OBJET de langage, ni la partie impérative. N’espérez donc pas voir une seule structure conditionnelle (if) ou itérative (while/for) dans ce texte.

Propositions : définitions et expressions :

Tout comme en mathématiques chaque phrase est une proposition, en ocaml chaque “ligne” est une proposition. Elle commence avec le premier caractère et se termine avec le symbole ‘;;’

Il y a deux types de propositions :

En ocaml, la syntaxe est la suivante :

L’aspect fonctionnel

Mais comment un tel langage peut-il exister, et surtout, être ‘utilisable’ sans pour autant faire usage des structures des langages impératifs comme le C ou le Python? (Je m’adresse bien sûr ici à ceux qui n’ont pas encore été initiés aux joies des langages fonctionnels)

Tout d’abord, définissons ce qu’est un langage fonctionnel. Par analogie à un langage objet où les éléments sont des objets(Dans certains cas, TOUS les éléments), dans un langage fonctionnel, les éléments sont des fonctions, au sens mathématique du terme.

Si l’on considère la fonction suivante : \(\begin{array}{ccccc} f & : & \mathbb{Z} & \to & \mathbb{Z}^2 \\ & & x & \mapsto & (x, x) \end{array}\)

C’est la fonction qui associe à un entier \(x\) son couple situé sur sa diagonale (injection diagonale) \((x,x) \in \Delta\mathbb{Z} \subset \mathbb{Z}^2\)

Il est alors très aisé de définir f en ocaml :

let f x = (x, x);

Cette fonction est de type int -> int * int, c’est-à-dire qu’elle prend un entier \(int \approx \mathbb{Z}\) et retourne un couple \((x, y) \in \mathbb{Z} \times \mathbb{Z}\).

On peut bien sûr définir des fonctions de fonction. Observons la fonction “composition”(rond) : \(\begin{array}{ccccc} o & : & (\mathbb{B} \Rightarrow \mathbb{C} ) \times (\mathbb{A} \Rightarrow \mathbb{B} ) & \longrightarrow & (\mathbb{A} \to \mathbb{C} ) \\ & & (f, g) & \longmapsto & (x \mapsto f(g(x)) \end{array}\).

En OCAML, on aurait :

let o f g = f g;;
(* val o : ('a -> 'b) -> 'a -> 'b = <fun> *)

Les filtres

Je vous ai parlé un peu plus tôt de l’absence de structures conditionnelles et itératives, je vais ici vous exposer les solutions à votre disposition.

Commençons avec les blocs conditionnels. En OCAML, nous disposons d’une fonctionnalité très intéressante : les filtres. Si l’on considère la fonction continue suivante : \(\begin{array}{ccccc} f & : & \mathbb{Z} & \to & \mathbb{Z} \\ & & x & \mapsto & \left\{ \begin{array}{ccc} x < 0 & \Rightarrow & -2x \\ x \ge 0 & \Rightarrow & x^2 \end{array} \right. \end{array}\)

On constate que l’on a bien deux condition, une première qui regroupe les cas des x négatifs, et une deuxième qui s’intéresse aux x restant. En OCAML, on définit de la même façon :

let f = function
  | x when x < 0 -> -2 * x
  | x when x >= 0 -> x*x
  ;;

Ou bien, en omettant la deuxième condition et en considérant que la dernière condition matchera pour “tous les cas restants”

let f = function
  | x when x < 0 -> -2 * x
  | x -> x*x
  ;;

Dans le cas où l’on n’a pas besoin de nommer x, on peut utiliser _. Ainsi, un dernier cas _ -> smtg correspondrait un peu à ‘default’ d’un switch. On pourra aussi utiliser l’instruction match {variable} with {filtre}.

La récursivité terminale

Maintenant, considérons les itérations. Nous allons utiliser la récursivité terminale (tail-rec pour les intimes) afin de construire une boucle. La récursivité terminale est une forme de récursivité où l’unique action qui succède à l’appel à la fonction récursive est un retour de valeur.

C’est plutôt élémentaire :

let rec aff_n = function
  | n when n < 1 -> ();
  | n -> print_endline (string_of_int n);
           aff_n (n - 1);;

() représente le type ‘unit’, c’est-à-dire que notre fonction ne retourne rien, tout comme print_endl. Nous obtenons une boucle de 10 à 1, et nous affichons les valeurs à l’écran. Vous vous demandez alors, pourquoi de la tail rec et non une simple récursivité? La réponse est que l’interpréteur/compilateur optimise par une itération, ce qui est bien plus pratique pour nos machines qui ont une mémoire limitée et ne peuvent supporter qu’un nombre restreint d’appels récursifs.

Pour conclure

OCAML est un langage fonctionnel, qui comporte bon nombre d’aspects et concepts qui sont parfois inconnus des programmeurs ayant pratiqué uniquement des langages impératifs, et constitue de par son existence une forme de culture dont il peut être bon d’avoir connaissance.

Références :

Si vous êtes curieux et désirez en savoir plus, voici quelques liens :