Le langage CAML est un langage qui a été écrit par l’INRIA, et qui a pour but de se calquer exactement à l’écriture mathématique. Pour plus de renseignements, ou pour vous procurer CAML, allez voir le site de l’INRIA

CAML est un langage intéressant du point de vue syntaxique et surtout du point de vue des filtres, qui sont d’une incroyable puissance. On y trouve aussi une gestion remarquablement pratique des listes chainées.

Le langages utilisé dans le programme des classes prépa est CAML-light, langage gratuit. une version plus performante Objective-CAML existe aussi. CAML existe sur diverses plateformes. La version Windows de CAML Light ne brille pas par sa réalisation : on y trouve une fenêtre d’exécution, dans laquelle le code et le résultat apparaissent. La fenêtre directement située en dessous est la fenêtre d’édition du programme. Celle à droite est constituée de l’historique des commandes lancées, très pratique pour récupérer du code accidentellement effacé. Il est très pénible d’utiliser cet éditeur pour des programmes devenant conséquents. Pour cela, on trouve quelques éditeurs séparés, qui rendent l’édition facile, et qui savent appeler CAML pour l’exécution. A noter que le BlocNote et un bon copier-coller suffisent amplement !

La version Linux est nettement plus agréable. Elle fonctionne de la même façon que tous les langages traditionnels, avec l’utilisation d’un éditeur séparé (XEmacs par exemple, qui sait faire la mise en évidence de la syntaxe pour CAML), le compilateur en ligne, et l’utilisation éventuelle, mais très agréable de l’outil make.

Concernant les performances de calculs, il faut éviter l’idée reçue que le langage CAML est lent. Par exemple, Objective CAML se classe de façon très honorable par rapport à ses concurrents traditionnels dans l’étude http://www.bagley.org/~doug/shootout/craps.shtml. N’essayez surtout pas de programmer en CAML comme vous programmeriez en C, ce n’est pas du tout la même logique et vous aboutiriez forcément à des résultats décevants.

Les dernières versions de CAML intègrent aussi des possibilités d’interface graphique avec GTK, de liens avec les base de données, et aussi de compilation en ByteCode (comme Java) recompilé sur le client. Il devient alors un langage multiplateforme intéressant.

Pour télécharger CAML, vérifiez qu’il ne soit pas déjà présent dans votre distribution Linux, ou allez faire un tour sur le site de l’INRIA. Il est possible d’obtenir gratuitement CAML par correspondance, en leur écrivant, et joignant les timbres nécessaires.

Rapide Initiation

Une petite initiation au CAML. Et plutôt aux particularités intéressantes de CAML.

CAML est un langage très typé, pour lequel, variables, fonctions et procédures sont des cas particuliers de types. Ceci est très intéressant. Prenons un exemple : une variable entière sera du type int, une fonction prenant deux int et envoyant un int sera du type (int*int)->int ou (int->int)->int. Ce deuxième type est plus subtile, comme vous le constaterez par la suite. Une procédure est une fonction qui ne renvoie rien, donc le type, pour une procédure qui prend un entier en paramètre : int->unit. (unit désignant le type nul, comme void en c)

L’instruction de base est let. C’est lui qui va permettre les affectations et les déclarations de fonctions. ` let x = 1;; ` va assigner 1 à la valeur x. Les variables ont besoin d’être déclarées par let avant de pouvoir être utilisées sans, par exemple : x = x + 1;;
Vous avez pu constater les deux point virgules. Ils désignent la fin de la ligne. Si vous vous demandez, mais pourquoi donc deux point-virgules… et bien tout simplement parce qu’un seul point virgule est le délimiteur d’instruction dans un bloc d’instruction.

Maintenant passons à l’écriture d’une fonction. Supposons, que nous souhaitions calculer factoriel n.

 let rec fact = function
  | 1 -> 1;
  | n where (n>0) -> n*fact(n-1);
  | _ -> 0;;

Regardons un peu de plus près ce que signifie ce code. Tout d’abord, nous reconnaissons le let de déclaration. Le mot clé rec inséré entre let et le nom de la fonction signifie que cette fonction est définie de manière récursive. Attention il est important, sans cela, s’il existe une définition en cours de fact, les références à fact à l’intérieur de cette fonction prendrait en compte les spécifications de la fonction précédente et non celles de la fonction en cours de défintion. ensuite on trouve l’identificateur de fonction ‘function’. Il existe d’autres méthodes équivalentes de défintions de fonctions que nous verrons par la suite. Juste après, nous voici directement en présence de ce constitue l’intérêt de CAML : j’ai nommé le filtrage.

En effet ces barres obliques signifient un cas de filtrage. Chaque cas de filtrage doit commencer par |, dire l’expression correspondant au cas recherché, puis renvoyer une valeur située après ->. La flèche -> veut correspondre aux notations des mathématiciens qui à x associe y, en symbolisant cette phrase par f : x -> y. Nous retrouverons cette flèche dans d’autres cas de fonctions. Un caractère intéressant de CAML est que on n’a pas eu du tout besoin de lui dire quoi que ce soit quant aux types des variables. Et pourtant, lorsque nous lançons le programme, il affiche :

 fact : int -> int = fun

Comment a t’il donc fait pour se rendre compte que nous opérions sur des entiers. L’analyseur de CAML a juste regardé le code : il a vu, pour les paramètres d’entrée, que leur nombre était reduit à un, puisqu’il n’y avait qu’un seul argument dans le filtrage. La première ligne de filtrage contenait la valeur 1, ce qui impliquait une valeur entière. Pour l’argument en sortie, même chose. Mais si nous supprimions la première ligne du filtre, comment ferait-il ? Tout simplement en regardant les opérations faites sur ces variables. Selon les types acceptées par les fonctions utilisées, CAML est en mesure de déterminer le type des variables. Par exemple, les opérateurs +, -, *, /, sont uniquement réservés aux entiers. Pour travailler sur des réels, il faudra utiliser, +., -., *., /., dédiés aux réels. Il est donc très important de bien regarder la sortie de CAML pour savoir si CAML a bien compris, et si vous n’avez pas fait d’erreurs, car CAML ne transige pas beaucoup avec les types incompatibles.

Pour revenir au filtrage, CAML lit les lignes de filtrages les unes après les autres, et renvoie le résultat de la première ligne qui convient à l’entrée. L’ordre des lignes est donc capital : si nous mettions la ligne | 1 -> 1 ; derrière | n -> …, celle-ci ne serait jamais exécutée, puisque le filtrage | n -> convient pour 1. En effet, | n -> signifie de prendre le premier argument quel qu’il soit et de le nommer n. Sa valeur sera donc utilisable dans la suite du programme en écrivant n. La clause where est très pratique, car elle permet de préciser une condition supplémentaire. Il faut toutefois s’en méfier, car elle n’existe pas dans tous les langages CAML. (des professeurs de prépa titilleux pourraient d’en offusquer, il vaut mieux prendre la précaution de mettre “NB : L’utilisation de where dans ce programme pourrait être remplacée par des clauses if, mais a été conservée à des fins de lisibilité.”). Enfin le filtrage | _ -> correspond en quelque sorte à une clause ‘sinon’, et valide n’importe quelle entrée. Il est toujours prudent de mettre un tel filtre et de renvoyer un message d’erreur ou une valeur montrant que les conditions des filtres précédent n’ont pas été remplies.

Voila pour notre premier programme. Examinons maintenant comment déclarer des variables dans une fonction. Cela se fait simplement par let in et where :

 let valA=3 and valB=4 in 
   ajoutesept x = x + valA + valB;

  ou encore

 let ajoutesept x = x + valA + valB
   where valA = 3 and valB = 4;


Les deux utilisations sont pratiques, selon le sens dont on a besoin. Mais pour l’instant, ce sont des variables plutôt constantes… En effet, leur valeur ne peut pas être changée par un simple valA = 4… Pour pouvoir changer, il faut faire appel aux références. Les références sont l’appellation CAML des pointeurs des autres langages. En définissant une référence vers un emplacement mémoire, vous n’avez pas la possibilité de modifier la référence, mais son contenu. Comment accéder aux références ? On les déclare et on les utilise simplement par

 
 let a = ref 0;;
 !a;;
 a:=4;;
 !a;;


Par ref 0, on définit une référence sur 0, donc une valeur entière. Par !a, on accède à la valeur pointée par la référence. Par a:=, on modifie la valeur pointée par la référence. Voila, en fait c’est tout simple !

La dernière chose que je ne peux taire ici est la gestion des listes sous CAML. Il faut d’abord bien comprendre que les listes chaînée sont de nature récursives, et donc seront très facilement utilisables par des fonctions récursives, alors que les vecteurs (nom des tableaux en CAML, de type ‘a vec, par exemple int vec) sont plutôt utilisés dans des fonctions itératives. C’est donc tout naturellement que toutes les fonctions que nous allons créer seront récursives.
Une liste t::q est constitué d’une tête t, qui contient la première valeur de la liste, puis une queue q, qui contient la liste des autres éléments. si vous voulez le deuxième éléments de cette liste, il vous faudra prendre la tête de la queue q… Que ça a l’air compliqué ! Mais regardons tout de suite en quoi la puissance des filtrages de CAML rend les choses aisées. Voici une fonction qui va retourner le n ième élément de la liste :

 let rec get = function
  | (t::q,0) -> t;
  | (t::q,n) -> get (q,n-1);
  | ([],n) -> failwith ("indice hors limite");
  [ _ -> faliwith ("paramètres non valides");

let l = 1::5::8::3::[];;
get (l,2);;

Et si tout marche bien vous devriez obtenir 8… Le moyen de procéder habituel est donc d’opérer sur l’élément de la tête d’une liste, puis si l’élément ne convient pas, garder et opérer sur la queue de cette liste. La liste vide est [], la liste réduite à un élément est [a], ou a::[]. On peut définir une liste avec a::b::c::d::[], ou par [a;b;c;d]. Cette dernière notation est à utiliser avec précautions, car les confusions avec la définition d’un vecteur [a,b,c,d] sont très fréquentes… La fusion de deux listes se fait à l’aide de @. (exemple, renverser une liste avec une fonction récursive renverse basée sur | t::q -> renverse(q)@\[t\]; )

Voila, vous connaissez l’essentiel à savoir sur les listes. Toutes les fonctions sur les listes dont vous aurez besoin sont très facilement réalisable en quelques lignes (quelques unes d’entre elles sont ci-dessous). A noter l’instruction map, qui permet d’appliquer une fonction à tous les éléments d’une liste, ça pourra va servir un jour peut-être…

J’en ai fini de cette courte présentation de CAML, et j’espère vous avoir donné l’envie d’en savoir plus. N’hésitez pas à regarder l’aide, et de nombreux tutoriels CAML très bien faits (en tout cas, meilleurs que le mien 🙂 sont disponibles. Vous y découvrirez les types, les boucles, les blocs, et les objets, pour lesquels le filtrage de CAML prend toute sa puissance…

Quelques programmes en vrac

Voici quelques programmes que j’ai écrits pendant les cours de CAML, à titre d’exercices…

Note : Le programme “Mini compilateur FORTH” précédemment présent sur cette page a été écrit par mon professeur de CaML de l’époque Alain Chillès, et non pas par moi, comme je l’ai cru quelques années plus tard. Je lui présente toutes mes excuses.