Premiers pas avec Objective-Caml


précédentsommaire

II. Premier contact

II-A. Installation

Pour installer Objective Caml, rien de plus simple si vous êtes sous Windows : il suffit de télécharger l'application, la copier dans son espace de travail et lancer le programme d'installation, par exemple en double-cliquant sur l'icône apparaissant dans l'explorateur de dossiers. Des indications devraient apparaître ainsi dans une boîte de dialogue : laissez-vous guider.

Notez bien qu'il existe plusieurs ports d'Objective Caml pour Windows : si vous ne savez pas et s'il s'agit de la première fois que vous l'installez, choisissez la version standard Win32. Certaines fonctionnalités, telles la possibilité d'utiliser le compilateur natif, ne sont pas disponibles, à moins de disposer de Visual C++, mais il est tout de même possible de créer des programmes binaires s'exécutant sans machine virtuelle préalablement installée, via ocamlc (voir l'option -custom). C'est une bonne distribution pour se faire la main avec le système Objective Caml.

Sous Linux, et uniquement pour les architectures du type x86, plusieurs packages existent, et donc leur installation ne devrait pas poser de problèmes (encore plus simple que sous Windows, hein !). Bien-sûr, il est toujours possible d'installer le tout à partir des sources !

Sous Unix, le meilleur moyen est d'installer la distribution à partir des sources. Les fichiers INSTALL et README que l'on trouve à la racine décrivent très bien les différentes étapes de l'installation ainsi que les outils nécessaires. Remarquez qu'il est hautement préférable avoir un système disposant des outils GNU afin d'exploiter au mieux certaines optimisations du code de la machine virtuelle, écrite en C, entre autres. Seules une commande ./configure et quelques appels à make devraient suffire pour installer le tout. Veillez bien à passer par toutes les étapes de l'installation, à moins que vous ne vouliez une installation un peu particulière : l'installation consiste, entre autres, au boot-strap des compilateurs et de tout le système. Cela prend un certain temps, mais si toutes les phases ne sont pas exécutées, le système ne sera pas complet. Il peut aussi être intéressant de jouer sur les différentes options du script ./configure, entre autres pour décider du compilateur C utilisé et du dossier cible.

En bref, une installation complète Objective Caml se compose des éléments suivants :

  • ocaml : la boucle d'interaction
  • ocamlc : le compilateur code objet
  • ocamlopt : le compilateur natif
  • ocamlrun : la machine virtuelle
  • ocamllex et ocamlyacc : les générateurs respectivement d'analyseurs lexicaux et syntaxiques
  • ocamldep : le calculateur de dépendances entre unités de compilation
  • ocamlbrowser : un petit environnement de développement dédié à Objective Caml
  • ocamldoc : un générateur de documentation
  • ocamldebug : un débugeur puissant
  • ocamlprof : un profiler afin de d'analyser les temps d'exécution de chaque partie du programme
  • camlp4 : une librairie permettant de traiter des syntaxes mais aussi le puissant pré-processeur d'Objective Caml
  • Pervasives : la librairie de base
  • la librairie standard comportant une quarantaine de modules
  • plusieurs librairies annexes, qui peuvent, selon les systèmes et les installations, ne pas être présentes :
    • Unix : programmation système portable !
    • Num : nombres de précision arbitraire
    • Str : expressions rationnelles
    • Threads
    • Graphics
    • Dbm : bases de données NDBM
    • Dynlink : chargement dynamique de code dans la machine virtuelle
    • LablTk : interface graphique Tcl/Tk
    • Bigarray : grandes matrices et grands vecteurs

Les noms des binaires se terminant par .opt correspondent aux versions compilées avec le compilateur natif, plus performantes. Les distributions binaires d'Objective Caml, en général, ne distribuent que ces dernières. Certaines distributions n'incluent pas tous les programmes : se reporter donc à la documentation fournie.

II-B. La boucle d'interaction

La boucle d'interaction d'Objective Caml est souvent, à tort, considérée comme le cœur du langage. En réalité, il ne s'agit que d'un interprète de commandes qui se contente d'évaluer les expressions entrées au clavier, comme ce que l'on peut trouver en MATLAB, Haskell avec Hugs et GHCi, MAPLE ou Scilab. L'utilisation la plus courante est lors de la phase de développement d'un projet, afin de tester ponctuellement certaines fonctions ou fonctionnalités. Cependant, elle permet également d'apprendre le langage en toute tranquillité, car elle fournit un environnement sûr, tolérant vis-à-vis des erreurs, chose qui n'existe pas en C ou en Java, par exemple.

Pour lancer la boucle d'interaction sous Windows, cliquez sur le chameau dans l'onglet Objective Caml du menu Démarrer, ou sur l'icône de bureau si vous en avez installé une. Sous Linux et Unix, entrez la commande ocaml. Que ce soit sous Windows ou sous Linux et Unix, une chose du genre de ce qui suit devrait alors apparaître à l'écran.

 
Sélectionnez

[InOCamlWeTrust @ Sahara Documents]$ ocaml
        Objective Caml version 3.09.2

#

Le symbole # indique le prompt, c'est-à-dire l'espace où l'utilisateur pourra entrer une expression à évaluer. Un session sous la boucle d'intéraction est donc constituée d'une suite d'expressions que l'on cherche à évaluer... voire à exécuter. On peut bien-sûr utiliser des résultats précédemment calculés, du moment qu'on les a liés à un identificateur au moyen d'une expression de la forme let ident = exp;;. On peut également saisir des directives, pour par exemple exécuter un script contenu dans un fichier, changer de dossier de travail ou tout simplement sortir de la boucle d'interaction. Un exemple de session est fourni ci-après.

 
Sélectionnez

[InOCamlWeTrust @ Sahara Documents]$ ocaml
        Objective Caml version 3.09.2

# 5;;
- : int = 5
# let a = 7;;
val a : int = 7
# type t = int;;
type t = int
# print_endline "Hey ! The Caml is looking at You !";;
Hey ! The Caml is looking at You !
- : unit = ()
# #quit;;
[InOCamlWeTrust @ Sahara Documents]$

Sous la boucle d'interaction, les expressions à évaluer se terminent toujours par ;;. Si ;; est omis, un retour à la ligne s'effectuera et l'utilisateur pourra saisir la suite de son expression sur la ligne suivante. La séquence ;; marque la fin de l'expression et donc le début de l'évaluation.

 
Sélectionnez

[InOCamlWeTrust @ Sahara Documents]$ ocaml
        Objective Caml version 3.09.2

# let b = true;;
val b : bool = true
# let a =
    if b || false then
        5
    else
        1
  ;;
val a : int = 5
# #quit;;
[InOCamlWeTrust @ Sahara Documents]$

Des informations de contexte sont affichées à gauche du résultat. Dans l'exemple ci-avant, on peut donc voir que b est du type bool et qu'il s'agit d'une valeur, comme spécifié par le mot-clef val, par opposition au type t défini dans l'exemple précédent, pour lequel la boucle d'interaction affiche le fait qu'il s'agit bel et bien d'une déclaration de type, indiqué par le mot-clef type, ici un alias pour le type des entiers standards.

Lorsque l'évaluation n'est associée à aucun identificateur, donc lorsque le résultat n'est pas conservé dans le contexte d'exécution courant, la boucle affiche un tiret - pour indiquer l'absence d'une telle association. L'évaluation de l'entier 5 en est un exemple, tout comme l'affichage du message "Hey ! The Caml is looking at You !". A ce titre, on remarquera qu'y compris le fait d'afficher un message à l'écran, par exemple, est une valeur, munie d'un type : le type unit dont toute valeur se réduit, après évaluation, à la valeur dénotée par (). Ce point sera abordé plus loin dans le tutoriel.

Ecrire tout un programme dans une boucle d'interaction se révèle très vite être une très mauvaise méthode. Il vaut mieux, au début, faire un script avec l'ensemble des expressions que l'on souhaite utiliser, charger ce script, puis faire appel à ces valeurs depuis la boucle d'interaction. Un script s'écrit de la même façon que l'on rentre des expression dans la ligne de commande : la fin de l'évaluation d'une expression est repérée par ;;. Lorsqu'un script est chargé dans la boucle d'interaction, toutes les valeurs sont évaluées, et le résultat est affiché sur le terminal, comme si on les avait rentrées une à une, à la main, sans pour autant que leur définition, c'est-à-dire le code, ne soit visible à l'écran. Voici un exemple de script : il reprend les expressions du premier exemple.

 
Sélectionnez

(* Cette ligne est un commentaire Objective Caml et n'est pas prise en compte *)

(* Remarque : les commentaires peuvent s'imbriquer (* les uns dans les autres *) *)

5
;;

let a = 7
;;

type t = int
;;

print_endline "Hey ! The Caml is looking at You !"
;;

Après avoir chargé le précédent script (de nom "script.ml" ici) dans la boucle d'interaction, on peut voir apparaître un écran du type suivant.

 
Sélectionnez

[administrateur@localhost Tutoriel]$ ocaml
        Objective Caml version 3.09.2

# #use "script.ml";;
- : int = 5
val a : int = 7
type t = int
Hey ! The Caml is looking at You !
- : unit = ()
#

La boucle d'interaction affiche alors les valeurs présentes dans l'environnement de travail : il s'agit ici de a et du type t, donc les valeurs avec lesquelles on peut poursuivre la session.

On rappelle enfin deux directives utiles : #use "nom_de_fichier.ml" pour charger un script, et #quit pour quitter.

II-C. Premières fonctions, notions de syntaxe Objective Caml

Après avoir fait un tout petit tour d'horizon de la boucle d'interaction, il est bon d'avoir quelques rudiments de syntaxe Objective Caml. Remarquons une chose : la syntaxe Objective Caml est très particulière, et demande l'usage de quelques parenthèses (mais pas beaucoup, en fait).

Tout, absolument tout est une valeur en Objective Caml... sauf les types, bien-sûr !

Une fonction, par exemple, est une valeur, tout comme un entier ou le simple fait d'afficher une chaîne de caractères à l'écran : la fonction d'affichage est une valeur, la chaîne de caractères en est une deuxième et le résultat, c'est-à-dire l'affichage, en est une troisième ! Ceci permet beaucoup de souplesse au moment d'écrire du code, car le niveau d'abstraction est assez élevé.

Les entiers sont de type int, les flottants de type float, les caractères de type char et les chaînes de caractères de type string. Tous ces types de valeurs s'écrivent de façon classique.

 
Sélectionnez

# 666;;
- : int = 666
# 3.1416;;
- : float = 3.1416
# 'a';;
- : char = 'a'
# "Hardware tabulations rule !!!";;
- : string = "Hardware tabulations rule !!!"
# print_endline "\'\\t\' rules !!!";;
'\t' rules !!!
- : unit = ()
#

Les appels de fonctions se font sans les parenthèses, comme dans l'exemple ci-dessus avec la fonction print_endline permettant d'afficher une chaîne de caractères avec un retour à la ligne en fin de chaîne. C'est l'une des particularités des langages fonctionnels issus de la théorie du lambda-calcul : une telle notation permet, en effet, de créer des applications partielles facilement, comme on le verra dans la suite du tutoriel.

Pour nommer une valeur, on utilise la construction let ident = exp in exp'. La valeur exp est alors repérée par le nom ident dans la sous-expression exp'.

Notez bien qu'il ne s'agit en aucun cas de la déclaration d'une variable : la valeur ident n'est pas modifiable... jamais !

C'est encore l'une des particularités des langages fonctionnels : les identificateurs repèrent des valeurs et non des variables... d'ailleurs, la notion de variable n'existe pas. Cependant, rien n'empêche au contenu de la valeur d'être modifiable ! En effet, il existe plusieurs types physiquement modifiables en Objective Caml, comme on le voit par ailleurs.

La construction let ident = exp in exp' est très différente de let ident = exp;;. La première lie exp à ident dans l'expression exp', tandis que la deuxième effectue la liaison dans tout le programme. En fait, la deuxième constitue en soi une phrase Objective Caml, alors que la première n'est qu'une expression, de valeur exp', de sorte qu'il est possible d'écrire des associations imbriquées.

 
Sélectionnez

# let a = 7 in
        let b = a + 8 in
                a * b
  ;;
- : int = 105
#

La valeur 105 est non liée et définie à l'aide de deux constructions let ident = exp in exp' imbriquées, mais on aurait également pu écrire la chose de la façon suivante, si on avait voulu en faire une valeur présente dans l'environnement d'exécution.

 
Sélectionnez

# let n =
        let a = 7 in
                let b = a + 8 in
                        a * b
  ;;
val n : int = 105
#

Si on veut effectuer plusieurs associations indépendantes, on peut utiliser la construction let ident1 = exp1 and ident2 = exp2 ... in exp dans des valeurs, ou let ident1 = exp1 ... and identn = expn;; pour une phrase Objective Caml.

 
Sélectionnez

# let hello = "Hello " and ker = "Kernighan " and amp = "& " and rit = "Ritchie " and bang = "!" in
        hello ^ ker ^ amp ^ rit ^ bang
  ;;
- : string = "Hello Kernighan & Ritchie !"
# hello;;
Unbound value hello
# ker;;
Unbound value ker
# amp;;
Unbound value amp
# rit;;
Unbound value rit
# bang;;
Unbound value bang
# let caml = "Caml" and haskell = "Haskell";;
val caml : string = "Caml"
val haskell : string = "Haskell"
# caml ^ " & " ^ haskell;;
- : string = "Caml & Haskell"
#

On aura remarqué que les valeurs hello, ker, amp, rit et bang sont uniquement visibles depuis l'expression hello ^ ker ^ amp ^ rit ^ bang, et ne sont pas utilisables par la suite, contrairement aux valeurs caml et haskell. L'opérateur ^ permet de concaténer deux chaînes de caractères.

On peut écrire des expressions conditionnelles grâce à la construction if cond then exp else exp', où cond est nécessairement du type bool. Les opérateurs booléens sont || pour le OU logique, && pour le ET logique et not pour le non logique. Les deux valeurs booléennes sont true et false. Les opérateurs de comparaison sont ceux utilisés habituellement : =, <, etc...

 
Sélectionnez

# let v = true and f = false;;
val v : bool = true
val f : bool = false
# if v || f then
        'V'
  else
        'F'
  ;;
- : char = 'V'
# let c =
        if v && f then
                'V'
        else
                'F'
  ;;
val c : char = 'F'
#

On peut déclarer une fonction de la façon suivante : let ident arg1 ... argn = exp;; si on veut rendre la fonction ident visible globalement ou let ident arg1 ... argn = exp in exp' si on veut qu'elle ne soit accessible uniquement depuis l'expression exp'.

 
Sélectionnez

# let est_pair n =
        if (n mod 2) = 0 then
                true
        else
                false
  ;;
val est_pair : int -> bool = <fun>
# let somme a b = a + b in
        somme 5 6
  ;;
- : int = 11
#

Toutes les expressions en Objective Caml sont typées et il n'existe pas de conversion implicite. De ce fait, tous les opérateurs arithmétiques existent pour les valeurs de type int et de type float : il suffit de les suffixer avec un point ., par exemple + et +., - et -., * et *., etc...

On peut néanmoins utiliser les deux fonctions de conversions prédéfinies suivantes.

 
Sélectionnez

# float_of_int;;
- : int -> float = <fun>
# int_of_float;;
- : float -> int = <fun>
#

Pour le système d'inférence de types, les opérateurs sont également des fonctions, et il est donc possible de les redéfinir. En effet, si on ne peut modifier une valeur, il est toutefois possible de créer un nouvelle valeur de même nom, auquel cas l'ancienne valeur est perdue, même si cela est très fortement déconseillé ! Un exemple amusant est celui-ci...

 
Sélectionnez

# 4 - 2;;
- : int = 2
# 4 * 2;;
- : int = 8
# 4 / 2;;
- : int = 2
# let ( - ) = ( + ) and ( * ) = ( + ) and ( / ) = ( + );;
val ( - ) : int -> int -> int = <fun>
val ( * ) : int -> int -> int = <fun>
val ( / ) : int -> int -> int = <fun>
# 4 - 2;;
- : int = 6
# 4 * 2;;
- : int = 6
# 4 / 2;;
- : int = 6
# #quit;;

Les fonctions ont également un type : le type de chaque argument apparaît devant une flèche, et le dernier type est celui de la valeur de retour de la fonction.

 
Sélectionnez

# let somme_mixte a x b = float_of_int (a + (int_of_float x) + b)
  ;;
val somme_mixte : int -> float -> int -> float = <fun>
#

a et b sont des entiers, x et la valeur de retour sont des flottants.

On peut également définir des fonctions récursives. Le mot-clef rec doit apparaître après le let.

 
Sélectionnez

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

Les entiers sont de largeur fixe (31 bits sur les machines 32 bits et 63 bits sur les processeurs 64 bits... le bit manquant étant dû au garbage collector), de sorte que des problèmes de débordement peuvent être visibles très tôt avec une telle fonction.

 
Sélectionnez

# fact 15;;
- : int = -143173632
# fact 16;;
- : int = -143294464
# fact 17;;
- : int = -288522240
#

Les fonctions peuvent aussi être mutuellement récursives : on emploie alors les constructions let rec ident1 arg1 ... = exp1 and ident2 arg2 ... = exp2 ... in exp' ou let rec ident1 arg1 ... = exp1 and ident2 arg2 ... = exp2 ... ;;.

Il existe aussi la syntaxe adaptée à la manipulation des structures impératives : boucles, affectations, etc... Ce point est abordé plus en avant, mais on donne ici deux exemples.

 
Sélectionnez

# let n = ref 0;;
val n : int ref = {contents = 0}
# for i = 1 to 10 do
        (print_int i;
        print_newline ();
        n := !n + i)
  done
  ;;
1
2
3
4
5
6
7
8
9
10
- : unit = ()
#

Les références, telles que n, sont modifiables grâce à l'opérateur :=, et la valeur d'une référence est obtenue grâce à l'opérateur !. Les instructions sont les valeurs de type unit et peuvent s'enchaîner avec le point-virgule ;. L'indice d'une boucle for n'est jamais modifiable.

On peut également utiliser des boucles conditionnelles.

 
Sélectionnez

# let n = ref 10;;
val n : int ref = {contents = 10}
# while !n > 0 do
        (print_int !n;
        n := !n - 1;
        print_newline ())
  done
  ;;
10
9
8
7
6
5
4
3
2
1
- : unit = ()
#

Comme dit plus haut, tout ceci est abordé dans la suite. On en donne ici deux exemples afin de montrer très explicitement que Objective Caml n'est pas un langage de programmation entièrement dédié à la récursivité.


précédentsommaire

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

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 Antonio Pasinelli. 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.