Non, là ton compilateur ne vérifie rien : si tu enlèves les lignes de gestion d'erreur, ton implémentation en C++ compilera quand même (et ça plantera à l'exécution, quand tu la lanceras sur un programme zlang qui contient des erreurs). Par ailleurs, il se trouve qu'effectivement c'est nettement moins élégant que dans un langage comme OCaml, où on pourrait écrire en simplifiant :
1
2
3
4
5
6
7
8
9
10
11
12 | type result =
| Ok of int
| Error
let div(result1, result2) =
match (result1, result2) with
| Ok a, Ok b ->
if b = 0
then Error
else Ok (a / b)
| Error, _
| _, Error -> Error
|
Le type result
(une sorte d'énumération) décrit des valeurs qui sont en gros « soit un entier n avec le tag Ok
, soit le tag Error
tout seul ». La fonction div
prend en paramètre deux résultats, et si ces résultats sont bien deux entiers (c'est ce que vérifie le match
), renvoie le résultat de leur division avec l'étiquette Ok
(sauf si le deuxième est nul, auquel cas elle renvoie une erreur). Si un des deux résultats passés en paramètre est lui-même une erreur, alors la fonction renvoie une erreur (attention, Error
est une valeur du langage, pas une erreur qui fait planter le programme). Le type de div
est alors result * result -> result
Le compilateur va s'assurer pour moi que j'ai bien traité les cas d'erreur possibles : si j'oublie dans les cas du match
ceux qui correspondent à une erreur sur un des deux résultats, il m'avertira (par contre, la division d'OCaml renvoie une exception en cas d'erreur, donc je suis obligé de faire la vérification b = 0
moi-même, et ça le compilateur ne m'y force pas).
Alors bien sûr, écrit comme ça ça ne fait pas forcément envie, parce que ça paraît un peu lourdingue à manipuler. En pratique on définirait une fonction qui factorise un peu la vérification des erreurs, et ça ressemblerait à ça :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | let ( >>= ) result f =
(* L'opérateur qui factorise la gestion des erreurs *)
match result with
| Ok x -> f x
| Error -> Error
let division a b =
(* Une fonction qui prend deux entiers (sans étiquette) et renvoie le résultat de la division *)
if b = 0
then Error
else Ok (a / b)
let div2 result1 result2 =
(* Une fonction qui prend deux résultats et renvoie un résultat *)
result1 >>= fun a ->
result2 >>= fun b ->
division a b
|
Si tu n'as jamais fait de programmation fonctionnelle typée, tu risques d'avoir un peu de mal à comprendre ce code là. L'important, c'est la dernière fonction (div2
), qui correspond exactement à la fonction div
de tout à l'heure, mais écrite de façon beaucoup plus lisible (on peut la lire comme « si result1
contient l'entier a
, et si result2
contient l'entier b
, alors je renvoie le résultat de la division de a
par b
» (et les cas d'erreur sont gérés tout seuls par >>=
).
Pour la culture, ce genre d'interface (le type + la fonction >>=
) est (une partie de) ce qu'on appelle une « monade ». Les haskelleux en sont fan et pensent que c'est une solution magique à tout un tas de problèmes, mais maintenant tu sais que c'est aussi simple que ça