IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Le langage fonctionnel Caml

Support de cours basé sur Caml Light


précédentsommairesuivant

VI. Traitement des exceptions

En programmation fonctionnelle, les fonctions sont totales, c'est-à-dire qu'elles sont applicables à tout argument qui appartient à leur type de départ. Il faut donc être capable de traiter les cas, appelés exceptions , où cet argument n'est pas acceptable. Par exemple : une division par zéro ou bien la recherche de la tête d'une liste vide. Ceci peut être fait par des tests préventifs placés dans le corps des fonctions, mais ce mécanisme est très lourd, car il implique un travail important pour le programmeur et il altère la lisibilité d'un programme en masquant son fonctionnement normal. C'est pourquoi les langages de programmation modernes comportent un mécanisme spécifique pour le traitement des exceptions. Dans ce chapitre nous étudions celui qui est offert par Caml.

VI-A. Déclenchement d'exceptions sans récupération

Considérons le programme suivant :

 
Sélectionnez
#let f x y = x / y;;
f : int -> int -> int = <fun>
#let g x y z = (x + y) / z;;
g : int -> int -> int -> int = <fun>
#(f 4 2) + (g 3 1 0);;
Uncaught exception: Division_by_zero

L'expression (f 4 2) + (g 3 1 0) n'a pas pu être calculée, car Caml a rencontré une division par 0 lors de l'application de g. Il a alors déclenché (on dit aussi «  levé  » ) l'exception Division_by_zero qui a été interceptée au niveau le plus haut (« top level » en anglais) ce qui a provoqué une interruption du programme signalée par le message Uncaught exception: Division_by_zero.

Ce mécanisme de détection des exceptions est tout à fait insuffisant pour les deux raisons suivantes :

  • le message généré par Caml ne permet pas de savoir en quel point du programme l'exception a été déclenchée. Dans l'exemple ci-dessus on ne sait pas si la division par zéro s'est produite lors de l'application de f ou de celle de g ;
  • le programme est interrompu brutalement sans qu'il soit possible de remédier à la cause de l'erreur pour que l'exécution puisse se poursuivre.

VI-B. Déclenchement d'exceptions avec récupération

Le mécanisme de traitement des exceptions de Caml peut être schématisé de la façon suivante (cf. Figure 6.1) :

Image non disponible

Figure 6.1 : Le mécanisme de traitement des expressions de Caml

  1. Lorsque le programmeur désire remédier aux erreurs pouvant se produire lors de l'évaluation d'une expression il peut encapsuler celle-ci dans un bloc de récupération d ' exception et déclencher une exception (!!!) si une erreur se produit.
  2. Un bloc de récupération d'exceptions ( B 1 et B 2) est composé de l'expression à évaluer et d'une fonction applicable aux exceptions pouvant être déclenchées dans ce bloc ( e 1 et f 1 pour le bloc B 1, e 2 et f 2 pour le bloc B 2).
  3. Lorsqu'une exception x est déclenchée ( 1 ) l'évaluation en cours est interrompue. La fonction de récupération f du bloc englobant le plus imbriqué est appliquée à x . La valeur de cette application remplace celle de l'expression dont l'évaluation a été interrompue.
  4. Si f n'est pas applicable à x alors x est à nouveau déclenchée ( 2 ) et le processus recommence à l'étape 3. Si le niveau le plus haut est atteint Caml interrompt l'évaluation et indique que cette interruption a été causée par une exception non récupérée (Uncaught exception) ( 3 ).

VI-B-1. Définition d'une exception

Caml offre un type union prédéfini exn dont les instances sont des exceptions. Les constructeurs d'exceptions sont définis incrémentalement par le programmeur au fur et à mesure de ses besoins.

Une exception est définie par la phrase :

 
Sélectionnez
exception C of t;;

C est le nom du constructeur de l'exception et t est une expression de type. Par exemple :

 
Sélectionnez
#exception Pile_vide;;
Exception Pile_vide defined.
#exception Erreur of string;;
Exception Erreur defined.
#Pile_vide;;
- : exn = Pile_vide
#Erreur "Division par zéro en appliquant g";;
- : exn = Erreur "Division par zéro en appliquant g"

Les valeurs Pile_vide et Erreur "Division par zéro en appliquant g" sont des instances du type exn.

Par ailleurs Caml possède un certain nombre d'exceptions prédéfinies : Failure, Match_Failure, Invalid_argument, etc.

VI-B-2. Déclenchement d'une exception

Le déclenchement d'une exception est réalisé :

  • soit par l'évaluateur de Caml, c'est le cas des exceptions prédéfinies comme Division_by_zero dont nous avons donné un exemple au §VI.A ;
  • soit par l'évaluation de l'expression :
 
Sélectionnez
raise e
  • où raise est une fonction prédéfinie et e est une expression de type exn.

Dans le programme suivant, par exemple, on déclenche une exception lorsqu'une pile devient vide :

 
Sélectionnez
#type 'a pile = Pile of 'a list;;
Type pile defined.
#let depiler = function
    | Pile (x::p) -> x
    | Pile [] -> raise Pile_vide;;
depiler : 'a pile -> 'a = <fun>
#depiler (Pile [1; 2]);;
- : int = 1
#depiler (Pile []);;
Uncaught exception: Pile_vide

Il est important de noter que le type d'arrivée de la fonction raise est un paramètre de type. Vérifions-le :

 
Sélectionnez
#raise;;
- : exn -> 'a = <fun>

Ceci, afin que l'insertion d'un déclenchement d'exception dans une expression n'ait pas d'influence sur le type de cette expression. Par exemple, si dans la fonction suivante :

 
Sélectionnez
#function x -> 365 / x;;
- : int -> int = <fun>

on insère un déclenchement d'exception en cas de division par zéro, on constate que le type de la fonction n'est pas changé :

 
Sélectionnez
#function x ->
    if x <> 0 then
        365 / x
    else
        raise (Erreur "Division par zéro");;
- : int -> int = <fun>

Il existe des exceptions prédéfinies de la forme Failure m (où m est une chaîne de caractères) qui sont déclenchées par l'application de la fonction prédéfinie failwith au messsage m . Par exemple :

 
Sélectionnez
#let debiter compte montant =
    if compte > montant then
        compte - montant
    else
        failwith "Credit insuffisant";;
debiter : int -> int -> int = <fun>
#debiter 23 54;;
Uncaught exception: Failure "Credit insuffisant"

VI-B-3. Récupération d'une exception

La récupération d'une exception déclenchée lors de l'évaluation d'une expression e peut être réalisée en encapsulant cette expression dans une expression try..with qui a la forme suivante :

 
Sélectionnez
try e with F1 -> e1 | &#8230; | Fn -> en

e , e 1 , …, en sont des expressions de même type et F 1 , …, Fn sont des filtres de type exn.

La valeur de cette expression est celle de e si aucune exception n'est déclenchée, sinon elle est celle de l'expression associée au premier des filtres F 1 , …, Fn qui filtre l'exception déclenchée.

La sémantique d'une récupération d'exception est la suivante.

TYPE ET VALEUR D'UNE RECUPERATION D'EXCEPTION. Si Env est un environnement et si e , e 1 , …, en sont des expressions telles que type (e, Env ) = type ( e 1 , Env ) = … = type ( e n , Env ) = t alors :
type (try e with F 1 -> e 1 | … | Fn -> e n , Env ) = t .
Si aucune exception n'a été déclenchée lors de l'évaluation de e alors :
val (try e with …, Env ) = val ( e , Env )
sinon, si une exception x a été déclenchée :
val (try e with F 1 -> e 1 | … | Fn -> e n , Env ) = val (match x with F 1 -> e 1 | … | Fn -> en | _ -> raise x , Env ).

En clair,

Par exemple, la fonction ajouter définie par :

 
Sélectionnez
#let ajouter n p = n + try depiler p with Pile_vide -> 0;;
ajouter : int -> int pile -> int = <fun>

ajoute à n le nombre placé au sommet de la pile d'entiers p. Dans le cas où la pile est vide, c'est zéro qui est ajouté à n . Vérifions-le :

 
Sélectionnez
#ajouter 5 (Pile [1]);;
- : int = 6
#ajouter 5 (Pile []);;
- : int = 5

précédentsommairesuivant

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2013 Jacques Le Maitre. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.