# Objective Caml/Modules

Cette page est considérée comme une ébauche à compléter . Si vous possédez quelques connaissances sur le sujet, vous pouvez les partager en éditant dès à présent cette page (en cliquant sur le lien « »).
Ressources suggérées : Aucune (vous pouvez indiquer les ressources que vous suggérez qui pourraient aider d'autres personnes à compléter cette page dans le paramètre « ressources » du modèle engendrant ce cadre)
Jusqu'à présent, nous avons vu comment il était possible d'utiliser OCaml en mode interactif. Ce mode est très utile pour apprendre OCaml, pour tester des extraits ou des fonctions existantes ou pour écrire des programmes qui se comporteront eux-mêmes comme OCaml en mode interactif. Cependant, ce mode n'est généralement pas approprié pour le développement de logiciels que vous voudrez distribuer, qu'il s'agisse d'applications bureau, d'applications serveur web ou d'utilitaires. Bien entendu, OCaml permet aussi d'écrire et de compiler des logiciels autonomes adaptés à toutes ces situations. Le mécanisme employé pour ce faire porte le nom de modules.
Les modules ont plusieurs utilités en OCaml. En plus de permettre de produire des applications exécutables, ils permettent aussi de diviser un programme long en plus petites unités, plus simples à gérer, à documenter et à partager entre plusieurs développeurs -- c'est le concept de modularité. C'est encore ce mécanisme qui est utilisé pour garantir l'indépendance entre parties d'un programme ou pour garantir que certaines parties du programme ne peuvent pas être manipulées par l'extérieur -- c'est la notion d'abstraction.
Ainsi, si nous considérons de nouveau l'exemple des nombres complexes, nous emploierons typiquement les modules pour les raisons suivantes :
* rassembler la définition du type « nombre complexe » et toutes les fonctions qui agissent sur ce type ;
* interdire à un utilisateur de construire un nombre complexe dont le module est strictement négatif ;
* normaliser les nombres complexes pour vous assurer que l'argument est toujours compris entre 0 et 2*pi, même si l'utilisateur a donné un argument inférieur ou supérieur.
De même, dans le cadre des opérations sur les fichiers, la bibliothèque standard d'OCaml utilise des modules pour les raisons suivantes :
* rassembler toutes les opérations sur les fichiers en un endroit, pour des questions de documentation et de cohérence dans les noms de fonctions ;
* vérifier une bonne fois pour toutes, lors de l'ouverture du fichier, si le fichier existe et si l'utilisateur a bien le droit de le lire ou/et de l'écrire ;
* cacher à l'utilisateur le type de données utilisé pour représenter les fichiers, de manière à ce que les développeurs de la bibliothèque standard puissent ultérieurement modifier ce type de données en étant certains de ne pas rendre incorrects les programmes développés à l'aide d'une version donnée de la bibliothèque standard.
Au cours de ce chapitre, nous allons construire et manipuler les modules, ainsi que les informations sur les modules. À partir du chapitre suivant, nous emploierons en permanence les modules fournis avec OCaml Batteries Included.

## Objectifs du chapitre

Ce chapitre vous enseignera
* comment écrire un programme complet
* comment compiler un programme
* comment diviser un programme en modules
* comment utiliser des modules existants, qu'ils aient été écrits par vous ou par les développeurs de la bibliothèque standard d'OCaml
* comment compiler une bibliothèque pour la distribuer séparément
* comment cacher les fonctions, types et valeurs privées d'un programme ou d'un module pour éviter qu'elles soient utilisées par d'autres modules
* comment générer la documentation d'un programme ou d'une bibliothèque.

## Conventions typographiques

Dans la suite de cet ouvrage, pour différencier les extraits tapés dans le mode calculatrice des codes sources sauvegardés dans des fichiers, nous emploierons deux styles différents.
Pour le mode calculatrice, nous noterons :
Le même programme, une fois sauvegardé, sera noté avec le style suivant :
Tout comme le symbole # du mode calculatrice ne doit pas être effectivement tapé, les numéros de lignes ne font pas partie du code source.

## Logiciels utilisés au cours de ce chapitre
### Compilation

Comme nous l'avons mentionné précédemment, OCaml est un langage compilé, c'est-à-dire un langage qui dispose des outils nécessaires pour transformer un code source en un fichier exécutable autonome. Plusieurs outils de compilation existent, qui à partir d'un fichier source peuvent produire soit un exécutable portable mais non-optimisé, comparable à du code compilé Java (ocamlc et ocamlc.opt), soit un exécutable optimisé mais réservé à une plate-forme, comparable à du code compilé C (ocamlopt et ocamlopt.opt), soit du code source utilisable avec une autre syntaxe (camlp4of, camlp4of.opt, camlp4rf, camlp4rf.opt), soit encore un exécutable modifié pour permettre l'analyse de performances (ocamlcp), de la documentation (ocamldoc) etc.
L'outil principal que nous allons manipuler dans le restant de cet ouvrage est OCamlBuild, dont le rôle est d'automatiser la construction d'un exécutable en gérant intelligemment tous les programmes précédents. Cet outil, comme les précédents, est fourni avec OCaml.
La manière la plus simple de compiler un fichier mon_fichier.ml à l'aide d'OCamlBuild est, dans une ligne de commande, de taper
Cette commande invoque la version de OCamlBuild pour OCaml Batteries Included en lui indiquant que nous voulons que, à l'issue de la compilation, le fichier fichier_de_destination soit créé. Selon le nom du fichier de destination, la compilation produira soit un fichier exécutable, soit une des informations résumant le contenu du programme, soit une documentation sous la forme d'une page web, etc.
Dans tous les cas, OCamlBuild examine tous les fichiers OCaml du répertoire, détermine les fichiers qui sont nécessaires pour compiler vos fichiers, lesquels de ces fichiers ont été modifiés et doivent donc être recompilés, puis se charge des étapes de compilation et de liaison. En cas de problème de compilation, OCamlBuild affiche des messages d'erreur comparables à celles de la ligne de commande OCaml.
Pour la majorité des projets, cette utilisation d'OCamlBuild suffit. Nous verrons plus tard comment demander à OCamlBuild de ne construire que certaines parties d'un projet incomplet ou comment générer de la documentation depuis un fichier source.

### Édition de code source

Vous pouvez écrire vos programmes OCaml avec n'importe quel éditeur de texte pour langues occidentales. Par contre, Word ou autre traitement de texte ne vous servira à rien.

#### Emacs 22

Emacs est un éditeur de texte pour programmeurs, extrêmement puissant, lui-même programmable et disposant de centaines d'extensions pour des langages de programmation ou des situations particulières. Emacs est disponible gratuitement sur à peu près toutes les plate-formes existantes.
Pour utiliser Emacs avec OCaml, vous aurez besoin de
* Emacs lui-même (téléchargement de la dernière version Windows -- en cas de doute, choisissez emacs-22.X-bin-i386.zip, téléchargement de la version MacOS X, si vous utilisez Linux, vous pouvez installer Emacs 22 depuis votre gestionnaire de paquets)
* l'extension Tuareg (téléchargement pour Windows ou MacOS X, si vous utilisez Linux, vous pouvez installer Tuareg depuis votre gestionnaire de paquets).
Une fois que vous avez installé et configuré Emacs et Tuareg, Emacs reconnaîtra et lancera automatiquement Tuareg dès que vous ouvrirez un fichier dont l'extension est .ml ou .mli.

##### Exécuter un extrait depuis Emacs

Une fois Tuareg lancé, vous pouvez demander au mode interactif de OCaml d'exécuter une commande en plaçant votre curseur sur la commande et en appliquant la combinaison de touches Contrôle-C-E. La première fois que vous invoquerez le mode interactif, Emacs vous demandera quelle version d'OCaml vous voulez employer.
Répondez

##### Compiler un projet depuis Emacs

De même, une fois Tuareg lancé, vous pouvez demander à Emacs de compiler les fichiers sur lesquels vous travaillez. Pour ce faire, appliquez la combinaison de touches Contrôle-C-C. La première fois que vous invoquerez le compilateur, Emacs vous demandera quel compilateur vous voulez employer.
Répondez
où XXXX est le nom du fichier compilé que vous souhaitez obtenir.

#### VI / Vim

VI / Vim est un autre éditeur de textes pour programmeurs, tout aussi puissant, programmable, extensible et disponible.
Pour utiliser VI avec OCaml, vous aurez besoin de
* VI lui-même (version Windows, version MacOS X, si vous utilisez Linux, vous pouvez installer Vim et/ou gvim depuis votre gestionnaire de paquets)
* l'extension OMlet (téléchargement pour Windows ou MacOS X, si vous utilisez Linux, vous pouvez installer OMLet depuis votre gestionnaire de paquets).
Si vous pensez que vous serez amené à éditer des fichiers en ssh sur un serveur, vim est un meilleur choix que emacs.

## Programmes complets

OCaml a une définition très simple de ce qu'est un programme complet : tout extrait de OCaml qui fonctionne en mode calculatrice, une fois sauvegardé dans un fichier avec l'extension .ml, est un programme complet valide.
Ainsi,
est un programme complet. On peut même d'ailleurs supprimer le ;;, qui n'est pas utile dans ce cas précis.
Pour utiliser ce programme, commençons le sauvegarder, sous le nom bonjour.ml.

### Lancer un programme

Nous pouvons maintenant le lancer notre programme ; pour ce faire :
* ouvrez un terminal (sous Windows, le terminal s'appelle « Invite de commande ») ;
* allez dans le répertoire dans lequel vous avez sauvegardé votre fichier bonjour.ml en tapant cd le/nom/du/répertoire ;
* tapez.
(le symbole $ est affiché par votre terminal, vous n'avez pas besoin de l'écrire — si vous êtes sous Windows, le symbole sera plutôt >)
vous verrez s'afficher
Félicitations, vous venez de lancer votre premier programme OCaml.

### Compiler et exécuter un programme

Il existe en fait plusieurs manières de lancer un programme OCaml. La manière directe, que nous venons de voir, s'appelle l'interprétation : OCaml lit le programme, vérifie qu'il a un sens, puis l'exécute immédiatement. Il s'agit de la manière la plus immédiate de tester si un programme s'exécute correctement mais le prix à payer est que le programme s'exécute plus lentement qu'avec les autres méthodes. De plus, dans un certain nombre de cas, il n'est pas raisonnable de demander à l'utilisateur, à chaque fois qu'il veut lancer son programme, de taper une ligne de code aussi surprenante que
L'alternative consiste à compiler le programme, c'est-à-dire à demander à OCaml de transformer votre fichier bonjour.ml en un autre fichier que l'ordinateur comprendra directement. Pour ce faire, toujours dans un terminal, tapez l'une des lignes suivantes :
ou
La première de ces lignes demande à OCamlBuild de construire bonjour.byte, c'est-à-dire un fichier exécutable portable, petit, rapide à compiler et théoriquement capable de fonctionner sur n'importe quel type d'ordinateur. La deuxième ligne demande à OCamlBuild de construire bonjour.native, c'est-à-dire un fichier exécutable optimisé pour un ordinateur précis et donc inutilisable sur tout autre type d'ordinateur, plus lent à compiler mais plus rapide à l'exécution.
Dans le reste de cet ouvrage, nous utiliserons généralement des fichiers portables (donc ici bonjour.byte). Ce choix est purement arbitraire.
Dans les deux cas, sauf accident, vous verrez apparaître un indicateur de progression puis, à l'issue de la compilation, un message de la forme
Ceci signifie qu'OCamlBuild a du produire 3 fichiers (dont bonjour.byte), qu'aucun de ces fichiers n'avait déjà été produit lors d'une compilation précédente, et que le temps total de compilation a été arrondi à 0 secondes.
   Anecdote OCamlBuild est un peu optimiste dans son affichage des durées de compilation.
Pour lancer le programme compilé, sous MacOS X ou Linux, il suffit alors d'écrire, toujours dans le terminal :
ou, sous Windows
De nouveau, le symbole $ ou < est inscrit par votre terminal, ce n'est pas à vous de le recopier.
Vous obtiendrez alors
Théoriquement, vous pourriez aussi ouvrir le programme depuis l'Explorateur de fichiers (sous Windows), le Finder (sous MacOS X), Konqueror, Nautilus ou votre gestionnaire de fichiers préféré (sous Linux), en ouvrant le répertoire qui contient bonjour.byte et en double-cliquant sur l'icône correspondante. Pour ce programme, qui se referme immédiatement dès qu'il a affiché une phrase, cela ne servirait à rien. Cependant, une fois que vous aurez doté votre programme d'une interface graphique ou textuelle, ce sera une manière de le lancer.
   Note Il existe encore d'autres manières d'exécuter un programme OCaml, notamment la possibilité de transformer votre fichier bonjour.ml en un script exécutable. Cette fonctionnalité n'est quasiment jamais utilisée car le programme ainsi lancé reste interprété et donc beaucoup plus lent qu'un programme compilé. Nous ne détaillerons donc pas la technique.

### Effets et résultats

Si nous avions tapé le contenu de bonjour.ml depuis le mode calculatrice, nous aurions obtenu quelque chose comme :
Ici, comme nous venons de le voir, le lancement de bonjour.ml, bonjour.byte ou bonjour.native a uniquement causé l'affichage de
De la même manière, dans le mode calculatrice, le nombre 5 est une expression valide, qui produira
Par contre, si nous exécutons le programme correspondant (donc un fichier qui contiendra en tout et pour tout 5), rien ne s'affichera. Cela ne signifiera pas qu'il y a eu le moindre problème -- de fait, le programme se sera exécuté correctement et aura un résultat. Par contre, comme nous n'avons pas précisé dans le programme que nous voulions afficher ce résultat, le programme n'aura aucun effet visible.
Pour faire afficher le résultat, nous aurions du écrire le programme suivant :
À l'exécution, ce programme calculera 5 et aura pour effet d'afficher cette valeur. Comme print_int a pour type int -> unit, ce programme aura de plus pour résultat, résultat qui ne sera lui-même pas affiché.
   Vocabulaire On désigne sous le nom d'effet observable ou, tout simplement, effet toute influence visible du programme sur le reste du monde. Ainsi, afficher un texte, ouvrir une boîte de dialogue, modifier un fichier ou envoyer un mail constituent autant d'effets. Par opposition, calculer le résultat d'une expression arithmétique (sans l'afficher) ou calculer la longueur d'une liste (sans l'afficher) n'a aucun effet. Par extension, on considère que lire un fichier, réagir à un clic ou télécharger une page web sont encore des effets.

### Précautions et limitations
#### Séparateurs

Dans le mode calculatrice, le symbole ;; sert à prévenir OCaml que vous avez fini de taper. Dans un fichier .ml, OCaml considère que vous avez fini de taper une fois qu'il atteint la fin du fichier. L'utilisation du ;; est donc beaucoup plus rare dans un fichier .ml que dans le mode calculatrice. On s'en sert sert surtout pour séparer deux expressions.
Ainsi, le programme suivant est tout à fait légitime
Ce programme commencera par calculer le résultat de 5 + 2, résultat qu'il n'affichera pas, puis il affichera le texte "Bonjour, le monde".

#### Fichiers portables

Sur le principe, les fichiers exécutables portables .byte que nous venons de voir et les fichiers modules portables .cmo que nous verrons bientôt sont comparables aux fichiers portables .class de Java.
En raison de priorités de développement différentes dans la conception du langage, les fichiers portables de OCaml sont malheureusement moins portables que leurs homologues en Java, puisque, s'ils sont compatibles avec tous les types d'ordinateur, ils ne sont compatibles qu'avec la version de OCaml et des bibliothèques de programmation qui ont servi à les construire.
L'une des conséquences de ces choix de développement est que, le plus souvent, un programme OCaml sera distribué soit sous la forme d'un exécutable natif optimisé, qui sera indépendant des bibliothèques installées, soit sous la forme de code source.

### À retenir

* N'importe quel code source OCaml peut être utilisé en tant que programme autonome.
* Un programme autonome n'affiche pas son résultat.
* Un programme autonome peut être compilé à l'aide de OCamlBuild ou exécuté à l'aide de ocaml.

## Modules

Comme la quasi-totalité des langages de programmation modernes, OCaml Batteries Included est fourni avec un grand nombre de structures de données et de fonctions de manipulation de données, sous la forme d'une bibliothèque standard. Ces structures et fonctions sont rassemblées selon leur rôle en modules. Ainsi, les fonctions qui servent à manipuler des listes forment le module List, les fonctions qui servent à afficher des informations à l'écran forment le module Print, les fonctions de manipulation de texte encodé en Latin-1 (caractères européens) sont rassemblées dans le module String, les fonctions de manipulation de texte encodé en UTF-8 (caractères internationaux) sont rassemblées dans le module UTF8, etc. Au cours de cette section, nous allons voir comment utiliser les fonctions d'un module puis comment créer de nouveaux modules.
   Note En OCaml, les noms de modules commencent toujours par une majuscule. Ceci est une obligation du langage.

### Utilisation d'un module

De base, OCaml est fourni avec plus d'une centaine de fonctions de manipulation des listes. Ainsi, pour calculer la longueur d'une liste, on invoquera la fonction List.length, c'est-à-dire la fonction length du module List :
De même, pour déterminer le dernier élément d'une liste, on emploiera la fonction List.last, c'est-à-dire la fonction last du module List :
List.length et List.last sont des fonctions parfaitement ordinaires, qui peuvent être manipulées comme toutes les autres fonctions. Ainsi, on écrira :
De la même manière, le module List définit un type 'a t, qui est identique au type 'a list et qui peut se manipuler comme tout autre type :
Nous reviendrons plus tard sur la raison pour laquelle OCaml a répondu Batteries.List.t et non List.t. Pour le moment, nous nous contenterons d'accepter que 'a Batteries.List.t et 'a List.t désignent bien les mêmes types.
Pour consulter la documentation sur un module, vous pouvez employer la directive #man_module. Comme toutes les directives, #man_module ne peut être utilisée que depuis le mode calculatrice, comme suit :
Vous pouvez aussi consulter directement la documentation d'une des valeurs ou d'un des types du module
Vous pouvez aussi consulter la liste des fonctions et types d'un module (sans documentation) à l'aide de la directive #browse, comme suit :
   Vocabulaire L'information qui vient d'être affichée sur le module List et qui commence par sig et s'achève par end s'appelle la signature du module. La notion de signature généralise aux modules la notion de type d'une valeur.
Vous constaterez que certaines fonctions du module List font appel à des types d'autres modules Enum et IO. Nous nous intéresserons à ces modules dans des chapitres ultérieurs. Vous constaterez aussi que le module List contient deux sous-modules Labels et Exceptionless, dont nous parlerons plus tard. Pour le moment, il suffit de savoir qu'un module peut contenir un sous-module. Comme pour les valeurs et les types, le nom complet d'un sous-module est le nom du module qui le contient, suivi d'un point, suivi du nom local du sous-module. Ainsi, le module Labels que nous avons sous les yeux a pour nom complet List.Labels et le module Exceptionless aura pour nom complet List.Exceptionless.

#### Ouverture de module

Considérons la fonction suivante :
Cette fonction prend en argument une list l, en extrait la liste l' des valeurs uniques de l (c'est-à-dire supprime les doublons), puis calcule la longueur de l', la divise par deux et se sert de cette valeur pour renvoyer la première moitié de la liste l'. Si cette fonction n'est probablement guère utile en pratique, nous pouvons constater qu'elle fait appel trois fois au préfixe List., peut-être aux dépens de la lisibilité.
Il serait certainement plus agréable de n'avoir à préciser qu'une seule fois que nous manipulons des fonctions du module List. C'est à cela que sert la commande open. Nous l'utiliserons comme suit :
Ou encore
Dans ces deux extraits, nous n'avons eu à mentionner List qu'une seule fois. Respectivement, ces extraits sont équivalents à :
ou à
Dans tous les cas, la fonction second_half obtenue est identique à celle que nous avions initialement. La seule différence est la notation plus légère.
   Vocabulaire L'opération open List est l'ouverture du module List. L'opération open List in (*...*) est l'ouverture locale du module List.
Pour des raisons de confort de notation, il est aussi possible d'ouvrir plusieurs modules en une seule opération. Ainsi, pour ouvrir le module List et le module String, on pourra écrire
On utilisera fréquemment ce mécanisme pour ouvrir en une opération le module List et son sous-module Labels, qui redéfinit certaines opérations de List pour permettre d'employer des notations plus robustes (mais plus lourdes), comme suit :
Nous reviendrons dans un chapitre ultérieur sur le rôle exact de ce module Labels.

#### Le module Standard

Au cours des chapitres précédents, nous avons manipulé un certain nombre de fonctions sans préciser de quel module elles sont tirées : print_endline, int_of_string, float_of_int etc. Ces fonctions et bien d'autres viennent d'un module spécial nommé Standard, qui est automatiquement ouvert. Ce module définit aussi de nombreuses fonctions de gestion des erreurs, d'affichage, de manipulation de fichiers, de calculs sur les entiers ou les nombres flottants, etc.
Comme pour les autres modules, vous pouvez consulter le contenu du module Standard à l'aide de #browse ou #man_module.

#### Précautions et limitations
##### Majuscules

Insistons sur le fait qu'un nom de module commence toujours par une majuscule. Ainsi, si vous écrivez List.length, OCaml sait qu'il doit chercher la fonction length du module List et, si nécessaire, préalablement charger ce module. À l'inverse, si vous écrivez list.length, OCaml sait qu'il doit chercher le champ length d'un enregistrement nommé list, ce qui est une opération beaucoup plus simple.
En particulier, les deux constructions produiront des messages différents :

##### Constructeurs et modules

De la même manière qu'un nom de valeur ou de type, un nom de constructeur doit être préfixé par le nom du module qui le contient.
Ainsi, le module Unix définit de nombreux constructeurs, un pour chaque erreur qui peut avoir lieu lors d'une interaction avec le système d'exploitation. Parmi ces erreurs, citons arbitrairement EBUSY, qui peut être invoquée lorsque le programme tente d'accéder à une ressource qui est déjà occupée par un autre logiciel. Comme nous pouvons le constater, le nom complet de cette ressource est Unix.EBUSY:
Ceci est aussi valable pour les enregistrements. Ainsi, le module Complex définit une structure type t = {re : float; im : float}. Le nom complet des champs est en fait Complex.re et Complex.im. Pour nous en convaincre, voici un exemple

##### Type t

Nous avons vu que le module List définit un type 'a t (le type des listes contenant des éléments de type 'a, c'est-à-dire un synonyme de 'a list). De la même manière, le module Int définit un type t (le type des entiers, c'est-à-dire un synonyme de int), le module Rope définit un type t (le type des textes contenant des caractères internationaux au standard Unicode), etc. De fait, une très grande partie des modules de OCaml Batteries Included définissent un type t. Il s'agit d'une convention de nommage simple : si un module est conçu spécialement pour accueillir une structure de données, le nom du type correspondant est t.
Afin d'éviter de confondre tous ces types qui portent le même nom, on prendra l'habitude d'utiliser le nom complet de chaque type, donc Rope.t, etc.

##### Batteries

Revenons sur un de nos exemples :
Comme nous pouvons le constater, OCaml a accepté cette définition mais la réponse mentionne Batteries.List.t et non List.t. Nous laissons au lecteur le soin de vérifier que ces deux types sont bien identiques.
À quoi est du ce renommage ?
De fait, le nom complet du module List est Batteries.List, ce qui signifie que le module List fait partie du module Batteries. Ce dernier est un module spécial, qui contient tous les autres modules de OCaml Batteries Included, y compris Standard et dont vous n'avez jamais besoin de taper le nom. Nous aurions cependant tout aussi bien pu taper :
En général, nous éviterons de mentionner explicitement Batteries, pour de simples raisons de lisibilité.

##### Ouverture et écrasement

Comme nous l'avons mentionné plus haut, l'ouverture d'un module définit implicitement de nombreuses valeurs. Ainsi, nous aurons
Dans cet extrait, la constante length a été écrasée par List.length lors de l'ouverture. Si ce comportement est parfois désiré, il s'agit aussi d'une source d'erreurs assez fréquente. Pour éviter tout accident, on préférera fréquemment employer l'ouverture locale, moins fragile puisqu'elle ne déborde pas d'un domaine visible au premier coup d'œil.

### Exercice

* Formulez une expérience pour permettre de vérifier que le type 'a List.t est bien le même que le type 'a Batteries.List.t.
* Écrivez une fonction d'une seule ligne qui renvoie le deuxième élément d'une liste. Pour ce faire, vous utiliserez uniquement les fonctions List.hd et List.tl.

### Créer de nouveaux modules

Bien entendu, il est possible d'écrire de nouveaux modules et de les utiliser comme les modules d'OCaml Batteries Included. Ceci se fait en deux étapes : définir le corps du module (c'est-à-dire les fonctionnalités du module) puis, si nécessaire, sa signature (c'est-à-dire les restrictions sur l'usage du module).
Commençons par nous intéresser au corps du module.
Créer un nouveau module peut se faire aussi simplement que d'écrire un programme complet. De fait, la seule différence entre un programme complet et un module est la manière de le compiler. Ainsi, pour créer un module on commencera par sauvegarder du code source dans un fichier d'extension .ml, que l'on compilera vers un fichier d'extension .cmo (pour obtenir un module portable non-optimisé) ou .cmx (pour obtenir un module portable optimisé). De plus, si vous ne souhaitez utiliser votre module que dans un programme compilé, vous n'avez pas besoin de le compiler du tout, OCamlBuild s'en chargera pour vous.
Ainsi, si nous souhaitons placer dans un module les fonctions que nous avons définies précédemment sur les couleurs de cartes couleur.ml. Comme ce module servira uniquement à accueillir une structure de données qui permettra de manipuler les couleurs de cartes, profitons-en pour renommer notre type de couleur_de_carte en t. Pour respecter les conventions de nommage habituelles des modules, nous en profiterons pour renommer quelques fonctions.
De la même manière, nous pouvons définir un nouveau module qui contiendra les fonctions de manipulation des cartes elles-mêmes. Appelons le fichier cartes.ml. Ce module utilisera le module Couleur, défini à l'aide du fichier précédent. Profitons-en de même pour renommer t notre type de cartes.
Si nous le souhaitons, nous pouvons maintenant compiler nos modules :
   Note L'ordre couleur.cmo carte.cmo est sans importance. Si un module dépend d'un autre, OCamlBuild compilera les modules dans l'ordre de dépendances.
Rappelons que nous n'avons pas besoin de compiler les modules nous-mêmes, à moins d'avoir envie de nous en servir en mode interactif, car OCamlBuild les compilera lui-même si nécessaire.
Testons maintenant nos modules dans un programme :
Compilons et testons :

#### Charger un module

Une fois qu'un fichier .ml a été compilé sous la forme d'un .cmo, nous pouvons le charger dans le mode interactif. Pour ce faire, nous utiliserons les directives #cd (pour aller dans le répertoire qui contient le fichier .cmo) et #load (pour charger effectivement le fichier .cmo). Si un .cmo dépend d'un autre .cmo, les fichiers doivent être chargés par ordre de dépendance.
Par défaut, OCamlBuild sauvegarde les fichiers .cmo dans un répertoire nommé _build. Nous pouvons donc faire
Comme l'indique ce dernier extrait, charger un fichier .cmo n'ouvre pas automatiquement le module. Il est donc possible de charger un module sans crainte d'écraser les noms de fonctions ou de types existants.
Une autre manière d'ouvrir un ou plusieurs fichiers .cmo est de donner la liste sur la ligne de commande
Vous disposez maintenant de toutes les connaissances nécessaires pour créer et utiliser un module. Dans la section suivante, nous allons voir comment et pourquoi restreindre l'accès à un module.
   Vocabulaire Indépendamment du langage de programmation, le fait de concevoir un programme comme un ensemble de modules dotés de dépendances simples et précises s'appelle la modularité.

#### Précautions et limitations
##### Un programme est un module

Insistons sur un point : un module peut contenir exactement le même genre de choses qu'un programme. Le fichier suivant, sauvegardé sous le nom bonjour.ml peut donc être compilé comme un module tout à fait acceptable :
Il s'agit d'un module qui ne définit aucune fonction, aucun type, mais qui, lorsqu'il est utilisé par un programme, affiche Bonjour, le monde.
Pour nous en convaincre, commençons par le compiler :
Puis chargeons ce fichier en mode interactif

##### Ordre de chargement

Insistons sur un point important : si les fichiers .cmo peuvent être compilés dans n'importe quel ordre, ils doivent être chargés par ordre de dépendance, c'est-à-dire en ne chargeant que des fichiers .cmo qui dépendent de modules déjà chargés.
Ainsi, l'extrait suivant charge les modules dans le bon sens :
Par contre, si nous essayons de charger carte.cmo avant couleur.cmo, nous obtiendrons :
Ce choix de conception du langage permet de garantir qu'un programme qui se charge peut aussi s'exécuter. Par contraste, des langages tels que Python offrent plus de souplesse au niveau du chargement mais un programme une fois lancé peut encore s'arrêter en catastrophe lorsque des modules s'avèrent manquants.

##### Dépendances cycliques

Une conséquence directe de cette contrainte sur l'ordre de chargement est qu'il n'est pas possible, en OCaml, d'écrire deux modules qui dépendent chacun de l'autre. Ainsi, il est impossible de compiler les deux fichiers suivants ensemble :
Si nous essayons de compiler l'un ou l'autre, nous obtiendrons
   Vocabulaire On appelle ce type de problème une dépendance cyclique.
Contrairement à ce qui est autorisé en Java ou en Python, il est impossible d'écrire un programme ou un module contenant des dépendances cycliques en OCaml. Cette limitation est liée directement à la contrainte sur l'ordre de chargement et au fait qu'un module peut contenir du code qui sera exécuté lors de son chargement. Si un programme contenait deux modules A et B mutuellement dépendants, ni le code de l'un ni le code de l'autre ne pourrait s'exécuter lors du chargement, puisque le chargement de chacun des deux nécessiterait le chargement préalable de l'autre.
Il est généralement admis en informatique qu'une dépendance cyclique entre deux sous-ensembles distincts d'un programme est une erreur de conception. Il existe une plusieurs techniques pour transformer un projet contenant des dépendances cycliques de manière à supprimer ces dépendances. La technique la plus simple, qui n'est pas toujours envisageable ou satisfaisante, consiste à isoler les fonctionnalités qui introduisent ces dépendances cycliques et à les rassembler dans un module propre, puis à masquer cette technique d'implantation à l'aide de la signature appropriée.
Une autre technique consiste à introduire de la récursivité mutuelle entre modules à l'aide d'un opérateur de point fixe sur module. Nous verrons cette technique bien plus tard, lors du chapitre consacré aux usages avancés des modules.

### Exercices

* Définissez un module Ast, qui regroupera les types programme, instruction... et les fonctions string_of_programme, string_of_instruction définis au chapitre précédent.

### Contraindre l'utilisation d'un module

Dans ce qui précède, nous avons vu comment créer des modules. Si ceci est déjà très utile, OCaml propose quelques concepts supplémentaires qui serviront notamment à documenter l'utilisation d'un module ou à la restreindre l'utilisation d'un module. Cette notion de restriction sur l'utilisation a de nombreux usages. On s'en servira d'une part pour garantir au développeur du module que des changements dans le fonctionnement interne du module n'interfèreront pas avec le fonctionnement des programmes qui utilisent ce module, et d'autre part pour garantir que l'utilisateur ne peut pas, par accident, introduire des valeurs incorrectes ou contraires aux hypothèses, telles qu'un 25 d'Atout dans notre jeu de tarot.
Pour introduire ces restrictions, on emploiera des signatures (ou interfaces).

#### Signatures

Un peu plus tôt dans ce chapitre, nous avons rencontré la signature du module List. À l'aide de la directive #browse, nous avions demandé à OCaml de nous afficher la liste des fonctions et types de ce module. Une signature n'est rien d'autre qu'une telle liste, sauvegardée dans un fichier d'extension .mli . Si nous désirons compiler une signature, ce qui n'est jamais nécessaire mais peut être utile le temps d'un test, il nous suffit de demander à OCamlBuild de produire un fichier d'extension .cmi
Ainsi, pour notre exemple du Tarot, nous allons commencer par écrire le fichier couleur.mli, qui accompagnera le fichier couleur.ml.
   Note OCaml cherchera toujours la signature correspondant à un fichier toto.ml dans le fichier correspondant toto.mli.
De la même manière, nous pouvons écrire le fichier carte.mli, qui accompagnera le fichier carte.ml :
À ce point, nous pourrions nous arrêter : nous disposons d'une interface complète pour chacun de nos deux modules, interface qui ne restreint rien. De fait, accordons-nous une petite pause, le temps de tester que tout ceci compile correctement :
Mais nous pouvons faire mieux.
Commençons par cette fonction string_of_chiffre_francais. Elle est certes nécessaire pour écrire la fonction to_string mais sa présence est plutôt embarrassante dans un module consacré à des cartes de tarot. Faisons-la donc simplement disparaître.
Voici donc une nouvelle version de carte.mli :
Comme nous pouvons le constater, cette fonction n'était effectivement pas nécessaire pour les utilisateurs du module Carte :
Maintenant que nous l'avons supprimée de l'interface du module, cette fonction ne peut plus être manipulée par les utilisateurs de ce module :
Lorsque nous avons supprimé la fonction de l'interface du module, nous avons interdit à tout utilisateur de la manipuler. Par contre, cette fonction est toujours présente dans le module, car elle est nécessaire à Carte.to_string, fonction dont nous venons de vérifier qu'elle est toujours présente.
   Vocabulaire La possibilité de cacher certaines parties d'un module pour empêcher leur utilisation à l'extérieur du module est une forme limitée d'encapsulation.
Maintenant que nous avons légèrement nettoyé l'interface, intéressons-nous un peu plus au cas des Atouts : un Atout est une carte dont le numéro est compris entre 1 et 22 -- ou entre 0 et 21, le choix doit être fait lors de la conception du module. À l'heure actuelle, rien ne m'interdit d'introduire, par accident, une valeur Atout 25. C'est malheureux et cela peut provoquer des bugs. Fort heureusement, nous pouvons corriger ce problème.
Pour ce faire, nous allons commencer par modifier l'implantation pour faire en sorte que le constructeur Atout n'utilise pas des entiers mais des valeurs d'un nouveau type, que nous allons appeler atout :
Bien entendu, pour que ceci compile, il faut que nous ayons préalablement défini le type atout. Comme nous souhaitons que les valeurs d'atouts soient bien des entiers, nous allons faire de atout un synonyme de int -- pour le moment.
Qu'avons-nous gagné pour le moment ? En apparence rien. En fait, l'intérêt de cette manipulation est que, si nous avons bien défini que le type atout est un synonyme de int, nous ne sommes pas obligés de rendre cette information publique. Ainsi, dans carte.mli, nous allons écrire, sans plus de détails :
   Vocabulaire Un type dont le nom est donné dans l'interface mais dont la définition complète reste cachée est appelé un type abstrait.
En rendant le type atout abstrait, nous avons interdit à l'utilisateur de fabriquer lui-même des valeurs de ce type. Il nous reste à définir deux fonctions triviales qui permettront de transformer un atout en int, ce qui peut servir pour comparer des valeurs d'atouts, ou pour transformer un entier compris entre 1 et 22 en atout :
Quel type donner à ces fonctions ? La fonction int_of_atout pourrait avoir pour type 'a -> 'a, tandis que atout_of_int pourrait avoir pour type int -> int option. Mais, comme nous avons en tête l'idée que la première de ces fonctions sert à transformer un atout en nombre entier et que la deuxième de ces fonctions sert à transformer un nombre entier en atout, nous allons leur donner les types suivants :
Nous disposons maintenant de deux fonctions qui permettent de manipuler les valeurs de type atout. Tant que nous n'ajoutons pas d'autre fonction qui renvoie une valeur de type atout, nous pouvons être certains que, <emph>par construction et malgré toute la mauvaise volonté du monde, aucun utilisateur n'arrivera à avoir un atout dont la valeur n'est pas comprise entre 1 et 22</emph>.
   Vocabulaire La valeur d'un élément de type atout est dite sure par construction, puis qu'il n'existe aucune manière pour un utilisateur de violer sa propriété fondamentale, qui est d'être comprise entre 1 et 22.
   Note En programmation fonctionnelle, on considère comme primordial cet question de sûreté par construction. L'essentiel du temps passé à concevoir une bibliothèque de programmation fonctionnelle est généralement consacré à essayer d'obtenir le plus de garanties de sûreté.
   Note Cette question de sûreté par construction est un très proche parent des capacités par objets présentes dans quelques langages de programmation conçus pour la sûreté (dont une variante de OCaml).
   Vocabulaire Profitons-en pour préciser que, indépendamment du langage de programmation, une bibliothèque est dite sûre s'il n'y a pas moyen de causer de comportements imprévisibles à l'aide de cette bibliothèque.
   Vocabulaire Un langage est dit sûr s'il est possible de programmer des bibliothèques sûres. On considère généralement que le langage C et les langages dynamiques ne sont pas sûrs, puisqu'il y a moyen de détourner totalement le fonctionnement d'une bibliothèque pour lui faire adopter un comportement imprévisible. Java, C#, OCaml, Haskell ou Coq seront des langages beaucoup plus sûrs. Entre eux, on considère généralement Java comme moins sûr que C#, lui-même moins sûr que OCaml, lui-même moins sûr que Haskell, lui-même moins sûr que Coq.
Nous pouvons encore améliorer légèrement les choses en autorisant l'utilisateur à utiliser soit 0 soit 22 pour l'excuse (carte qui, rappelons-le, peut avoir deux valeurs distinctes) mais en nous débrouillant pouru que les valeurs employées en internes soient toujours comprises entre 1 et 22. Pour ce faire, il nous suffit de remplacer la définition de atout_of_int par
De la même manière que nous avons introduit un type atout et des fonctions atout_of_int et int_of_atout pour les atouts dont la valeur doit être comprise entre 1 et 22 (ou 0 et 22), nous allons introduire un type nombre et des fonctions nombre_of_int et int_of_nombre pour les cartes numérotées, dont la valeur doit être comprise entre 2 et 10.
   Note Il n'est pas nécessaire de déclarer les types et valeurs dans le même ordre dans le fichier .ml et dans le fichier .mli correspondant.
Et voilà !

#### Documentation

L'habitude veut que tous les fichiers .mli soient documentés à l'aide de commentaires écrits selon quelques conventions simples. Ces commentaires pourront plus tard être extraits et transformés en documentation HTML ou PDF. Pour le moment, contentons-nous de quelques annotations, que nous complèterons un peu plus tard lorsque nous aurons introduit les outils d'extraction.
Commençons par le fichier couleur.mli :
Puis le fichier carte.mli :

#### Types privés

Nous venons de voir la notion de types abstraits, qui nous a permis de garantir que les cartes numérotées et les atouts étaient forcément des valeurs valides. Le mécanisme consiste à masquer toute information sur la manière dont est implanté un type, et à ne fournir que des fonctions sûres pour la manipulation des valeurs de ce type.
Dans certains cas, cela semble un peu exagéré. On pourrait préférer laisser publique l'information sur la manière dont est implanté un type et de se contenter de restreindre la possibilité de créer des valeurs de ce type. Ainsi, dans notre exemple en cours, la seule manière d'utiliser une valeur de type atout ou nombre est de commencer par la convertir en entier à l'aide de int_of_atout ou int_of_nombre. Nous gagnerions en simplicité (et peut-être en vitesse) si nous pouvions autoriser un programmeur à utiliser toute valeur de type nombre ou atout directement comme un int, à condition de disposer d'un mécanisme pour protéger la création des valeurs de type nombre ou atout.
C'est le principe des types privés.
Ainsi, dans carte.mli, nous pouvons remplacer
par
Dans le fichier carte.ml, nous pouvons de même nous débarrasser de int_of_nombre et int_of_atout, devenus inutiles.
Pour nous convaincre que ceci fonctionne correctement, commençons par compiler le tout :
Puis chargeons et testons dans le mode interactif :
Et voilà.

#### À titre de comparaison
##### Types abstraits

La notion de type abstrait apparait, sous une forme légèrement différente, dans la majorité des langages de programmation orientée objet statiquement typés.
Ainsi, nous pouvons considérer qu'un type abstrait est un type tel que :
* il est impossible de construire directement une valeur de ce type
* il est impossible de se servir d'informations sur la manière dont le type est effectivement implanté.
Dans un langage à objets, ces caractéristiques se traduiront par :
* un type abstrait est la donnée d'une interface d'objets
* la ou les classes qui implantent cette interface sont protégées.
En Java, pour implanter la première version de notre type nombre, nous écrirons donc :
En plus de ceci, de la même manière que nous avons fourni une fonction nombre_of_int en OCaml, nous devons fournir une fonction ou une méthode usine en Java, telle que :
La version Java est plus verbeuse et prête plus à confusion. En contrepartie, elle permet plus d'extensibilité et plus de modularité, puisqu'une implantation de Nombre peut être remplacée par une autre sans avoir à réécrire le code qui manipule les instances type Nombre.

##### Types privés

De la même manière que les types abstraits, les types privés apparaissent, sous une autre forme, dans les langages orientés objet.
Ainsi, nous pouvons considérer qu'un type privé est un type tel que :
* il est impossible de construire directement une valeur de ce type
* toutes les informations sont disponibles sur la manière dont le type est effectivement implanté.
Dans un langage à objets, ces caractéristiques se traduiront naturelle par une classe dotée d'un constructeur protégé. Ainsi, en Java, pour implanter la deuxième version de notre type nombre, nous écrirons

#### Exercices

* Donnez une signature à votre module Ast.
* Adaptez la signature et le module de manière à ce que nombre, text et nom soient des types abstraits. Pour l'implantation, pour le moment, vous emploierez respectivement les types concrets int, string et string.
* Adaptez le module Ast de manière à ce que les seules valeurs de type nom soient des chaînes de caractères qui ne contiennent que des lettres (donc ni espace ni ponctuation, etc). Pour ce faire, vous pourrez utiliser les fonctions
* Char.is_letter, qui vérifie si un caractère est bien une lettre
* String.length, qui renvoie la longueur d'une chaîne de caractères
* String.get, qui renvoie le nème caractère d'une chaîne.