Introduction
Les langages de programmation fonctionnelle et de
programmation impérative s'opposent principalement sur le contrôle
de l'exécution des programmes et la gestion mémoire des données.
-
un programme fonctionnel calcule une expression. Ce calcul donne
lieu à la production d'une valeur. L'ordre dans lequel les
opérations nécessaires à ce calcul sont effectuées et la
représentation physique des données manipulées sont
indifférents puisque le résultat est identique dans tous les
cas. Dans un tel cadre, la récupération de la mémoire est gérée
implicitement par le langage : on fait appel à un récupérateur
automatique de mémoire ou GC1.
- un programme impératif est une suite d'instructions modifiant
un état mémoire. Chaque étape de l'exécution est encadrée
par des structures de contrôle rigides indiquant la prochaine
instruction à exécuter. Les programmes impératifs manipulent plus souvent des
pointeurs ou des références sur des valeurs que les valeurs
elles-mêmes. Il faut alors allouer et récupérer
explicitement l'espace mémoire nécessaire au stockage des
valeurs. Cela entraîne parfois des erreurs d'accès mémoire. Rien
n'empêche néanmoins d'utiliser un GC.
Les langages impératifs fournissent un plus grand contrôle de
l'exécution et de la représentation mémoire des données.
Étant plus proche de la machine réelle, le code peut être
plus efficace mais perdre en sûreté d'exécution. La programmation
fonctionnelle, parce qu'elle offre un plus haut niveau d'abstraction,
procure un meilleur niveau de sûreté d'exécution : le typage
(dynamique ou statique) peut y être renforcé, évitant ainsi des
opérations sur des valeurs incohérentes ; la récupération automatique
de mémoire, quitte à perdre en efficacité, assure l'existence
effective des valeurs manipulées.
Historiquement, ces deux paradigmes de programmation se sont vus
consignés dans des univers distincts : les applications symboliques
pour le premier, et numériques pour le second. Mais certaines
choses ont évolué, en particulier les techniques de
compilation des langages fonctionnels et l'efficacité des
GC. D'autre part, la sûreté d'exécution est devenue un critère
important, voire prépondérant, pour la qualité d'une
application. En atteste << l'argument de vente >> du langage Java
selon lequel l'efficacité ne doit pas prendre
le pas sur la sûreté, tout en restant raisonnablement bonne. Et c'est une
idée qui progresse dans le monde des producteurs de logiciel.
Objective CAML se situe dans cette lignée. Il marie les deux paradigmes de
programmation permettant ainsi d'accroître son domaine d'application
en facilitant l'écriture d'algorithmes dans un style ou dans
l'autre. Il conserve néanmoins de bonnes propriétés de sûreté
d'exécution grâce à son typage statique, son GC et son mécanisme
d'exception. Les exceptions sont une première structure de contrôle
d'exécution explicite; elles permettent d'effectuer une
rupture/reprise de calcul. Ce trait est à la frontière de ces deux
modèles car bien qu'il ne change pas le résultat d'un calcul, il peut
modifier l'ordre d'exécution. Le fait d'introduire des
données physiquement modifiables peut changer le comportement de la
partie purement fonctionnelle du langage. En effet l'ordre
d'évaluation des arguments d'une fonction devient déterminant si
cette évaluation effectue des effets de bord.
Pour cette raison, de tels langages sont dits << langages
fonctionnels impurs >>. On perd en effet en niveau d'abstraction car le
programmeur doit tenir compte du modèle mémoire ainsi que du
déroulement du programme. Cela n'est pas toujours négatif, en
particulier pour l'efficacité du code écrit. Par contre, les traits
impératifs modifient le système de types du langage : certains
programmes fonctionnels, correctement typés d'un point de vue
théorique, ne le sont pas en pratique du fait de l'introduction des
références. Néanmoins, de tels programmes pourront se réécrire
facilement.