Découvrez Objective Caml

Objective Caml (Ocaml pour les intimes) est un langage de programmation magnifique: flexible, sûr, d'exécution rapide, doté de nombreuses bibliothèques (graphiques, réseau, ...). Il est souvent, à tort, considéré comme une curiosité qui n'intéresserait que quelques informaticiens théoriciens. Dans cet article, nous parcourrons rapidement l'arbre généalogique de ce langage pour en comprendre les principes fondateurs et familiariser le lecteur avec la programmation fonctionnelle; nous présenterons ensuite les différents modes de travail d'Ocaml et quelques aspects originaux de ce langage. Nous terminerons par un petit exemple d'utilisation des bibliothèques graphique et Unix et l'évocation des autres outils de développement disponibles pour Ocaml.

Linux Magazine France, numéro 43
Paru dans Linux Magazine
n° 43 d'octobre 2002

Introduction

Objective Caml est le résultat de plusieurs décennies d'évolution dans la conception des langages de programmation. Il partage avec son lointain ancêtre Lisp (créé au MIT dans les années 1960 et servant à programmer GNU Emacs) et son cousin éloigné scheme (utilisé dans The Gimp) la propriété d'être un langage fonctionnel, c'est-à-dire que les fonctions y ont un rôle privilégié comme nous le verrons plus bas.

Le langage C, créé au début des années 1970, n'a pratiquement pas évolué depuis. C'est indéniablement un langage aux multiples avantages, au premier rang desquels ses performances lors de l'exécution, mais il « manque de structure » en un certain sens. Comment être sûr, en effet, qu'un programme C ne terminera pas de façon inopinée par un segmentation fault ou réalisera bien la tâche qu'on en attend? On se contente souvent de quelques tests pour répondre à ces questions, sans produire la moindre garantie.

Même un langage récent comme Java souffre de carences analogues puisqu'on voit Microsoft annoncer dans ses licences (e.g. dans le paragraphe 9 du contrat de licence de Windows 2000) que son Sun l'oblige à mettre en garde l'utilisateur éventuel de Java sur ses risques potentiels. Voici donc ce que Microsoft écrit sur la technologie de son rival de toujours:

LE PRODUIT LOGICIEL PEUT INCLURE UNE PRISE EN CHARGE DE PROGRAMMES ÉCRITS EN LANGAGE JAVA. LA TECHNOLOGIE JAVA N'EST PAS TOLÉRANTE AUX PANNES ET N'EST PAS CONÇUE, FABRIQUÉE OU DESTINÉE À ÊTRE UTILISÉE OU VENDUE EN TANT QU'ÉQUIPEMENT DE CONTRÔLE EN LIGNE DANS DES ENVIRONNEMENTS À RISQUES DONT LES PERFORMANCES NE DOIVENT SUBIR AUCUNE DÉFAILLANCE, TELS QUE LES INSTALLATIONS NUCLÉAIRES, LA NAVIGATION AÉRIENNE OU LES SYSTÈMES DE COMMUNICATION AÉRIENS, LE CONTRÔLE DU TRAFIC AÉRIEN, LES APPAREILS DE RÉANIMATION OU LES SYSTÈMES D'ARMEMENT, POUR LESQUELS LES DÉFAILLANCES DE LA TECHNOLOGIE JAVA PEUVENT DIRECTEMENT PROVOQUER LA MORT, DES PRÉJUDICES CORPORELS OU DE GRAVES DOMMAGES MATÉRIELS OU À L'ENVIRONNEMENT.

L'interaction entre les mathématiques et l'informatique n'a pas cessé depuis l'émergence de cette dernière discipline et il n'est pas surprenant qu'on ait eu l'idée d'essayer de trouver un moyen de prouver mathématiquement qu'un programme fonctionnera et fera ce qu'on attend de lui, de manière à ce que la sûreté de fonctionnement ne repose plus uniquement sur des tests mais également sur des théorèmes. Si cette assurance est toujours la bienvenue, elle devient critique dans certaines circonstances comme lors du pilotage des centrales nucléaires et dispositifs médicaux sus-mentionnés... De telles garanties peuvent être exhibées pour des programmes Ocaml, en particulier lorsqu'il interagit avec des systèmes permettant à la preuve de programmes, comme le système Coq, développé par le Projet Logical de l'INRIA.

La notion de Méta Langage (ML), étudiée dès les années 1970 constitue un début de réponse à ce problème et la première implémentation de ce concept fut celle proposée à cette même époque par Robin MILNER: Standard ML.

Entre  1985 et 1990, une implantation efficace des concepts ML par les chercheurs du Projet CRISTAL de l'INRIA donna naissance au langage Caml (le mot « CAML » est un acronyme de « Categorical Abstract Machine Language »). Au début des années 1990, une implantation à machine virtuelle baptisée Caml-Light lui a succédé, elle-même désormais remplacée par Objective Caml qui permet, outre les traits impératifs, la programmation orientée objet. On dispose ainsi du style de programmation le mieux adapté à chaque situation.

Ocaml est en constant développement et bénéficie des dernières avancées de l'informatique théorique comme, par exemple, son remarquable GC (garbage collector ou glaneur de cellules: le gestionnaire automatique de mémoire).

Il se trouve que les chercheurs en informatique ne se sont pas exactement tourné les pouces ces trente dernières années alors pourquoi ne pas bénéficier de leurs efforts?

craps.jpg

Du point de vue de l'efficacité, le Shootout Scorecard de Doug BAGLEY (disponible sur http://www.bagley.org/~doug/shootout/craps.shtml) donne les résultats ci-dessus. Le code produit par le compilateur natif d'Ocaml il arrive en deuxième position un point derrière gcc) et devance de 8 points g++. Le code-octet d'Ocaml est 15 points devant le code natif produit par Java...

Distribution et installation

Je ne m'attarderai pas sur l'installation qui ne devrait pas poser de problème puisqu'elle est on ne peut plus standard. Sont disponible sur http://caml.inria.fr des binaires précompilés pour Linux, MS Windows 2000, NT, ME, 98, 95, Mac OS 9 et OS X et des distributions complètes avec sources sous forme de .rpm ou .tar.gz. Notez qu'Ocaml est souvent présent dans les distributions classiques de Linux (par exemple dans ma Mandrake 8.2), ce qui rend son installation d'autant plus facile.

Trois modes de travail

Les programmes écrits en Ocaml peuvent être compilés, soit vers du code-octet portable avec la commande ocamlc, exécutés par une machine virtuelle baptisée ocamlrun, soit vers du code natif avec la commande ocamlopt. Il est possible d'écrire directement du code Ocaml dans un terminal (ou mieux: dans Emacs avec le mode tuareg) dans lequel on aura préalablement lancé la boucle d'interaction Ocaml avec la commande ocaml qui compilera ce code à la volée.

La boucle d'interaction

Pour commencer, plaçons-nous dans un terminal et invoquons la boucle d'interaction, l'invite de notre shell devient un dièse:

$ ocaml
        Objective Caml version 3.04

# let x = 1 + 1 ;;
val x : int = 2
# #quit ;;
$

Comme on peut s'en douter, let x = 1 + 1 signifie « soit x le résultat du calcul 1 + 1 ». Cette instruction se termine par un double point-virgule (nous justifierons cette curiosité plus loin). La réponse d'Ocaml à cette instruction est la ligne val x : int = 2 qui se lit « la valeur x est de type int et vaut 2. ». Nous découvrons au passage deux caractéristiques d'Ocaml:

  • le langage est fortement typé: toute valeur manipulée par Ocaml a un type bien défini (ici entier); la vérification de la compatibilité entre les types permet d'éliminer la plupart des erreurs introduites par maladresse ou étourderie et contribue à la sûreté de l'exécution;

  • le typage est inféré automatiquement: l'utilisateur n'a pas à faire de déclarations de type, tous les types sont déduits automatiquement de la syntaxe des expressions.

Enfin, on quitte Ocaml avec la directive #quit. Celle-ci est préfixée par un dièse pour mettre en évidence qu'il s'agit d'une commande ne faisant pas partie du langage de description des programmes Ocaml mais destinée à contrôler le système Ocaml lui-même.

La boucle d'interaction est souvent utilisée pour développer rapidement une application en faisant quelques tests sans avoir à passer par le cycle écriture → compilation → exécution.

Un source Ocaml écrit dans un éditeur de texte pourra être, au choix, importé dans la boucle d'interaction avec la directive #use (voire copié-collé s'il ne s'agit que de quelques lignes), compilé vers du code-octet (bytecode) portable, ou vers du code natif.

Un point important est que le typage est statique, c'est-à-dire décidé à la compilation. Dès lors, il n'est pas nécessaire de faire ces vérifications durant l'exécution du programme ce qui accroît son efficacité.

La boucle d'interaction peut être personnalisée pour accéder aux bibliothèques. On crée alors, grâce à la commande ocamlmktop, une nouvelle boucle d'interaction avec les options qu'on aura choisies.

Le compilateur pour machine virtuelle

Illustrons cette compilation avec notre programme préféré:

$ cat > hw.ml
print_string "Hello World\n" ;;
^D
$ ocamlc hw.ml
$ file a.out 
a.out: a /usr/bin/ocamlrun script text executable
$ head -n 1 a.out 
#!/usr/bin/ocamlrun
$ ./a.out 
Hello World

L'instruction print_string écrit son argument (noter l'absence de parenthèses autour de celui-ci, nous y reviendrons) sur la sortie standard. Le programme a.out est généré par le compilateur ocamlc et sa première ligne invoque automatiquement la machine virtuelle ocamlrun.

Le compilateur natif

On compile le même fichier que ci-dessus avec le compilateur natif:

$ ocamlopt hw.ml 
$ file a.out
a.out: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically 
linked (uses shared libs), not stripped
$ ./a.out 
Hello World

Voilà le minimum vital. Passons maintenant au langage Ocaml proprement dit.

Aspects fonctionnels d'Ocaml

Qu'est-ce qu'une fonction?

Nous l'avons annoncé plus haut: Ocaml est un langage fonctionnel. Cela signifie en particulier que les fonctions sont des valeurs de première classe: elles peuvent être arguments d'autres fonctions, ou résultat d'un calcul.

Les programmeurs des langages impératifs (et objets) désignent généralement par fonction un gadget pratique pour regrouper et réutiliser des instructions qu'il serait fatigant ou inefficace de réécrire. Des variantes terminologiques existent: procédure lorsque les fonctions n'ont pas d'argument, méthodes lorsque ces fonctions sont attachées à des objets.

Il faut cependant prendre garde aux effets de bord, c'est-à-dire au fait que ces « fonctions » sont susceptibles de modifier un certain nombre de variables, explicitement ou pas. Prenons par exemple le programme C suivant:

$ cat > f.c
#include<stdio.h>
int main(void)
{
  int x = 0, y = 0;
  int f(int z){x++; return x+z;}
  printf("y=%d, f(y)=%d\n",y,f(y));
  printf("y=%d, f(y)=%d\n",y,f(y));
  return 0;
}
$ gcc -Wall f.c
$ a.out
y=0, f(y)=1
y=0, f(y)=2

Ni le code source de f, ni la valeur de y n'ont changé explicitement entre les deux printf. Mathématiquement, f n'est pas une fonction Z → Z (la lettre Z désigne l'ensemble des entiers), car sinon, on a la contradiction f(0) = 1 = 2.

En C, la syntaxe x = 0 suggère que l'« entier x est égal à 0 ». En réalité, il s'agit d'une affectation, c'est-à-dire la constitution d'un lien entre le nom x et un espace mémoire contenant la valeur 0. L'exécution de la fonction f modifier cette valeur. Après le premier appel de f, l'« entier x est égal à 1 »: cette « égalité » dépend du temps.

En Ocaml, le fait d'écrire let x = 0 ;; n'est pas une affectation au sens ci-dessus car aucune fonction ne pourra changer ce qui est une vraie égalité mathématique, indépendante du temps. En d'autres termes, cela signifie que x est un synonyme de 0. Seule une redéfinition volontaire et explicite (comme en mathématiques) comme, par exemple, let x = 1 ;; pourra changer la valeur de x.

La présence du double point-virgule souligne l'invariance dans le temps des égalités lorsqu'on adopte un style fonctionnel de programmation dans Ocaml tandis que le point-virgule simple séquence des instructions devant être exécutées dans un ordre précis lors de l'utilisation des traits impératifs d'Ocaml (boucles for et while, pointeurs, tableaux, etc), comme on le verra dans l'exemple complet à la fin de cet article.

En pratique, les effets de bord peuvent être pratiques (pour faire un tri sur place), voire indispensable (pour les entrées-sorties) mais il faut garder à l'esprit que la décision d'implémenter une fonction avec effet de bord affaiblit la structure mathématique du programme et donc la possibilité d'en prévoir le comportement.

Donnons maintenant un exemple de fonction Ocaml, la fonction successeur x ↦ x + 1:

# let succ = function x -> x + 1 ;;
val succ : int -> int = <fun>
# x ;;
Unbound value x

La ligne se lit « soit succ, la fonction qui, à tout x associe x + 1 ». Ocaml déduit de l'expression x + 1 que x est de type int et il infère automatiquement le type de la valeur succ: c'est une fonction de type int -> int, c'est-à-dire prenant un entier en entrée et renvoyant un entier en sortie. On note qu'une fois l'instruction exécutée, x n'est plus connu: il n'a un sens que dans portée de la définition de la fonction où il lie la valeur de sortie (x + 1) à celle de l'entrée x (bound est le participe passé du verbe to bind: lier). Une variante syntaxique est:

# let succ x = x + 1 ;;
val succ : int -> int = <fun>

Appliquons la fonction succ à un entier:

# succ 1 ;;
- : int = 2

Le tiret signifie que la valeur précédente n'est pas nommée. Notons d'ailleurs que les fonctions Ocaml n'ont pas besoin non plus d'être nommées (auquel cas, elles sont dites anonymes) pour être utilisées. On aurait, en effet, pu écrire:

# (function x -> x + 1) 1 ;;
- : int = 2

Le type unit est à peu près l'équivalent du type void en C. Par exemple, on a:

# print_string ;;
- : string -> unit = <fun>
# Random.bits ;;
- : unit -> int = <fun>
# Random.bits () ;;
- : int = 655733195

En effet, la fonction print_string affiche une chaîne de caractères et renvoie la valeur () qui est de type unit. La « procédure » Random.bits est une fonction qui a pour argument () et qui renvoie une suite de bits aléatoires sous la forme d'un entier (les entiers Ocaml sont codés sur 31 bits, afin d'exploiter le bit restant pour différencier valeurs immédiates de pointeurs lors de la récupération automatique de mémoire). La forme particulière du nom de cette fonction signifie qu'elle appartient au module Random.

Fonctions a plusieurs variables et curryfication

Lorsqu'on parle de fonctions « à plusieurs variables », on parle en fait de fonctions dont l'argument est un couple, un triplet, etc. En d'autres termes, l'ensemble de départ de la fonction est un produit cartésien. Par exemple:

# let add = function (x,y) -> x + y ;;
val add : int * int -> int = <fun>
# add (2,3) ;;
- : int = 5

La fonction add a pour argument un couple (x,y) qui est de type int * int (mathématiquement, cela veut dire que ce couple appartient au produit cartésien Z x Z).

Pour modéliser la notion de fonction à plusieurs variables dans les langages fonctionnels, on préfère définir des fonctions dont le résultat est une fonction (on dit qu'elle est curryfiée, du nom de Haskell CURRY (1900-1982) qui a popularisé cette méthodologie). Cela est justifié par la théorie mathématique principale dont sont issus ces langages: le lambda-calcul (le « lambda » provenant du fait que la lettre grecque λ apparaît tout le temps dans les expressions mathématiques qu'on manipule dans cette théorie). Ainsi, on préférera définir la fonction add_curry qui à tout x associe la fonction qui à tout y associe l'entier x + y:

# let add_curry = function x -> (function y -> x + y) ;;
val add_curry : int -> int -> int = <fun>
# add_curry 2 3 ;;
- : int = 5

Pour plus de lisibilité, on n'a pas de parenthèses, ni lors de l'affichage du type int -> int -> int qu'il faut lire: int -> (int -> int), ni dans l'appel de la fonction sur ses arguments qu'il faut lire ((add_curry 2) 3): en effet, la fonction (add_curry 2) est la fonction qui à tout entier y associe l'entier 2 + y; l'écriture ((add_curry 2) 3) est donc 2 + 3 = 5. Un avantage de la curryfication est qu'on obtient une fonction parfaitement valide en ne spécialisant qu'un seul des arguments; on peut, par exemple, retrouver la fonction successeur en écrivant:

# let succ' = add_curry 1 ;;
val succ' : int -> int = <fun>
# succ' 1 ;;
- : int = 2

On notera au passage que les identificateurs peuvent comporter le caractère apostrophe (sauf en première lettre où il a une signification différente) pour représenter un « prime ».

On préférera l'une des deux variantes syntaxiques suivantes à celle donnée plus haut:

# let add_curry = fun x y ->  x + y ;;
val add_curry : int -> int -> int = <fun>
# let add_curry x y = x + y ;;
val add_curry : int -> int -> int = <fun>

Variables locales

Un autre héritage du lambda-calcul est la façon de définir les variables locales:

# let x = 1 in x + 1 ;;
- : int = 2

L'expression se lit « soit x = 1 dans l'expression x + 1 », ou encore « soit l'expression x + 1 dans laquelle x vaut 1 ». Voici donc une fonction qui calcule le carré du maximum entre deux entiers:

# let f x y =
    let m = max x y in m * m ;;
val f : int -> int -> int = <fun>
# f 3 10 ;;
- : int = 100

Récursivité

La récursivité est l'outil de base en programmation fonctionnelle pour remplacer les boucles. On peut en effet toujours théoriquement implémenter avec des fonctions récursives les notions de boucle for et de boucle while. Prenons l'exemple de la fonction factorielle. En style impératif, on va utiliser une référence sur un entier qui sera modifié à chaque passage dans une boucle for:

# let fact n =
    let result = ref 1 in
      for i = 1 to n do
        result := i * !result;
      done;
      !result;;
val fact : int -> int = <fun>

Le point d'exclamation précédent la référence désigne la valeur qui lui est associée à cet instant et le := est bien un symbole d'affectation. Noter les points-virgule simples. En utilisant une fonction récursive, on évite les les effets de bord. Par ailleurs, beaucoup d'algorithmes, comme celui du calcul la fonction factorielle, sont naturellement récursifs et gagnent en clarté et en efficacité à être implémentés dans un langage dans lequel cette récursivité pourra s'exprimer. Ainsi, le code devient, en style fonctionnel:

# let rec fact n = if (n = 0) then 1 else n * fact (n-1) ;;
val fact : int -> int = <fun>
# fact 5 ;;
- : int = 120

Filtrage de motifs (pattern matching) et types somme

Le filtrage de motifs améliore considérablement l'expressivité d'Ocaml: c'est du switch de C:, en beaucoup plus puissant.

# let rec fact = function
    0 -> 1
  | n -> n * fact (n - 1);;
val fact : int -> int = <fun>

On peut définir des types somme, exploitables lors des filtrages. Par exemple (attention à la casse):

# type jour = Lundi | Mardi | Mercredi | Jeudi | Vendredi | Samedi | Dimanche ;;
type jour = Lundi | Mardi | Mercredi | Jeudi | Vendredi | Samedi | Dimanche
# let grasse_matinee = function
    Dimanche -> true
  | _        -> false ;;
val grasse_matinee : jour -> bool = <fun>
# grasse_matinee Dimanche ;;
- : bool = true
# grasse_matinee Lundi ;;
- : bool = false

Le trait bas (underscore) représente un élément quelconque mais comme le filtrage se fait séquentiellement, la fonction ne renvoie true que si son argument est Dimanche. Une variante syntaxique de la fonction grasse_matinee est:

# let grasse_matinee j = match j with
    Dimanche -> true
  | _        -> false ;;
val grasse_matinee : jour -> bool = <fun>

Le polymorphisme

Dans Ocaml, le typage est polymorphe paramétrique: l'algorithme de typage reconnaît le type générique d'une fonction dont les paramètres - dits polymorphes - ne sont pas totalement spécifiés; cela permet de développer un code générique réutilisable dans tous les contextes compatibles avec ce type polymorphe. Prenons le cas de la fonction List.map, qui applique une fonction sur une liste:

# List.map grasse_matinee [Lundi;Mardi;Mercredi;Jeudi;Vendredi;Samedi;Dimanche] ;;
- : bool list = [false; false; false; false; false; false; true]
# List.map succ [1;2;3;4;5] ;;
- : int list = [2; 3; 4; 5; 6]
# List.map ;;
- : ('a -> 'b) -> 'a list -> 'b list = <fun>

Les types préfixés par une apostrophe désignent des types quelconques (des variables de type), mais on constate qu'il y a une distinction entre 'a et 'b. En effet, si le premier argument de List.map prend des arguments d'un certain type 'a et renvoie des valeurs d'un certain type 'b, le deuxième argument de List.map doit être une liste d'éléments de type 'a et le résultat final sera une liste d'éléments de type 'b. La généricité est maximale tout en continuer de garantir la compatibilité des arguments.

Un « xclock -digital » personnalisé en Ocaml

Nous passons maintenant à un exemple concret: un petit programme qui ouvre une fenêtre X et y affiche la date et l'heure, à la manière de ce que ferait la commande xclock -digital mais sur deux lignes, et en français.

let width = 150 ;; (* largeur de la fenetre *)
let height = 50 ;; (* hauteur de la fenetre *)

(* On ouvre la fenetre: *)
Graphics.open_graph (" " ^ string_of_int(width) ^ "x" ^ string_of_int(height)) ;;

(* La police par defaut devient 9x15bold: *)
Graphics.set_font("9x15bold") ;; 

(* On definit un tableau des jours et des mois: *)
let days = [|"Dim";"Lun";"Mar";"Mer";"Jeu";"Ven";"Sam" |] ;;
let months = [| "Jan";"Fev";"Mar";"Avr";"Mai";"Jun";
                "Jul";"Aou";"Sep";"Oct";"Nov";"Dec"|] ;;

(* Boucle principale: *)
while true do
  Unix.sleep 1;
  Graphics.set_color Graphics.blue ;   (* couleur par defaut devient bleu *)
  Graphics.fill_rect 0 0 width height ;                (* dessine le fond *)
  Graphics.set_color Graphics.yellow ;(* couleur par defaut devient jaune *)
  let time = Unix.localtime (Unix.gettimeofday ()) in      (* On recupere:*)
    let h0 = string_of_int(time.Unix.tm_hour) and                (* heure *)
        m0 = string_of_int(time.Unix.tm_min) and               (* minutes *)
        s0 = string_of_int(time.Unix.tm_sec) and              (* secondes *)
        d0 = string_of_int(time.Unix.tm_mday) in          (* jour du mois *)
          (* on va eventuellement prefixer par zero, c'est plus joli: *)
          let time_string = 
            (if String.length h0 = 2 then h0 else "0"^h0) ^":"^ 
            (if String.length m0 = 2 then m0 else "0"^m0) ^":"^ 
            (if String.length s0 = 2 then s0 else "0"^s0) and
          day_string = 
            days.(time.Unix.tm_wday) ^" "^ 
            (if String.length d0 = 2 then d0 else "0"^d0) ^" "^
            months.(time.Unix.tm_mon) ^" "^
            string_of_int(1900+time.Unix.tm_year) in
              (* On centre et on dessine le tout: *)
              let (day_width,text_height) = Graphics.text_size day_string and
              (time_width,_) = Graphics.text_size time_string in
                Graphics.moveto (width/2 - day_width/2) (height/2) ;
                Graphics.draw_string day_string ;
                Graphics.moveto (width/2 - time_width/2)
                                (height/2 - text_height) ;
                Graphics.draw_string time_string ;
done;;

On compile ce programme avec le Makefile:

bytecode: xfrdate.ml
  ocamlc -custom graphics.cma unix.cma -o xfrdate xfrdate.ml -cclib -lunix -cclib -lX11

native: xfrdate.ml
  ocamlopt graphics.cmxa unix.cmxa -o xfrdate xfrdate.ml -cclib -lunix -cclib -lX11

clean:
  rm -f *.cmi *.cmo xfrdate


xfrdate.jpg

Une quarantaine de lignes d'Ocaml suffisent pour afficher ce programme graphique affichant la date.

Environnement de développement Ocaml

Outre les programmes que nous avons déjà cités (ocaml, ocamlc, ocamlopt, ocamlrun), il existe un environnement complet de développement comportant, entre autres:

  • un debugger (ocamldebug) permettant la pose de points d'arrêt, l'exploration de variables, pour trouver et réparer facilement les erreur et même remonter en arrière dans l'exécution (!) ;

  • un profiler (ocamlprof) permettant d'optimiser les programmes écrits en Ocaml;

  • un mode Emacs (tuareg) permettant de développer plus rapidement (colorisation automatique de la syntaxe, y compris lors de l'utilisation de la boucle d'interaction dans Emacs, indentation automatique, raccourcis-clavier, etc.

tuareg mode

Le mode tuareg est très pratique. La colorisation automatique de la syntaxe rend le développement plus aisé. Le raccourci clavier C-c C-s permet de lancer la boucle d'interaction dans Emacs, C-c C-k de la tuer et C-c C-e de lui envoyer les instructions du buffer principal contenant le source Ocaml.

Conclusion

Ocaml certainement l'un des meilleurs langages de programmation à l'heure actuelle. Il bénéficie à la fois d'une grande structure qui accroissent sa sûreté d'exécution, d'une syntaxe très expressive et d'une implémentation redoutablement efficace - comparable à celle de C dans beaucoup de circonstances - et à la pointe des dernières avancées de la recherche en informatique.

Au cours de ce long article, nous avons vus quelques principes de base du développement en Ocaml mais nous n'avons malheureusement même pas eu le temps de décrire son mécanisme d'exceptions, ou encore de programmation orientée objet incluant un système de modules permettant la dissociation de l'interface avec l'implantation ainsi qu'un système de classes avec héritage multiple.

Le langage Ocaml est activement développé à travers le monde et bénéficie de nombreuses bibliothèques: entiers en précision arbitraire, analyses lexicale et syntaxique, processus légers (multithreading), interface graphique, réseau,... On peut également, depuis Ocaml, appeler une fonction C et vice versa.

Ocaml est largement utilisé dans l'éducation (en classes préparatoires aux grandes écoles d'ingénieur et à l'université notamment) et la recherche mais également dans l'industrie. Nous espérons que la lecture de cet article vous a donné envie de rejoindre la communauté des hackers esthètes, programmeurs Ocaml.

Pour poursuivre cette découverte, le lecteur pourra se reporter aux ouvrages cités en référence et surtout se reporter à http://caml.inria.fr sur lequel il trouvera une multitude de liens, dont celui de la documentation en ligne d'Ocaml: http://caml.inria.fr/ocaml/htmlman/. Mentionnons enfin l'existence du newsgroup fr.comp.lang.caml pour les discussions exotiques.

Références

CHAILLOUX, Emmanuel, MANOURY, Pascal et PAGANO, Bruno. Développement d'applications avec Objective Caml. O'Reilly. 2000. Une version complète du livre (en langue française et anglaise) est disponible librement en ligne sur http://www.pps.jussieu.fr/Livres/ora/DA-OCAML/index.html.

COUSINEAU, Guy et MAUNY, Michel. Approche fonctionnelle de la programmation. Ediscience international. 1995.

PECQUET, Lancelot. Programmation Fonctionnelle. Introduction illustrée en Objective Caml. Disponible librement en ligne sur http://www-rocq.inria.fr/~pecquet/teach.html.

WEIS, Pierre et LEROY, Xavier. Le langage Caml. Dunod. 2ème édition. 1999.




Valid HTML 4.01!          Viewable With Any Browser          Lynx 2.8 compatible          Powered by Mimine 1.0