Objective Caml/Introduction
Qu'est-ce qu'OCaml ?
OCaml est un langage de programmation, c'est-à-dire une manière de donner des instructions à l'ordinateur pour obtenir des résultats ou des effets.
Tout comme Java, C# ou Python, OCaml est un "langage de haut" niveau conçu pour écrire des applications et des bibliothèques de programmation évoluées, sans avoir à se préoccuper de questions de bas niveau, telles que la gestion de la mémoire, et conçu pour favoriser la réutilisation de code ou de composants. Tout comme ces langages, OCaml dispose de nombreuses bibliothèques spécialisées qui permettent de manipuler des interfaces graphiques, des images 3D, de services web, des sons et musiques, des objets mathématiques...
Par opposition à Java, C# ou Python, qui sont des "langages impératifs", OCaml entre dans la catégorie des "langages fonctionnels". Là où un programme impératif sera constitué d'une suite de modifications de la mémoire vive et de l'état de l'ordinateur, un programme fonctionnel sera constitué d'un ensemble de fonctions mathématiques, qui permettent de déduire un résultat à partir d'entrées. OCaml est aussi un langage multi-paradigme, c'est-à-dire que, lorsque la programmation fonctionnelle ne suffit pas, il reste possible d'utiliser la programmation impérative ou/et la programmation orientée-objets, c'est-à-dire à se ramener à une programmation qui ressemblera aux langages précédents.
Les concepteurs de Java ou C# affirment que ces langages sont "statiquement et fortement typé", ce qui n'est que partiellement vrai. Par opposition, OCaml est effectivement "statiquement fortement typé", ce qui signifie que le langage de programmation procède automatiquement à de nombreuses vérifications et rejette les programmes considérés comme erronés ou programmés avec insuffisamment de rigueur. De plus, le typage d'OCaml se fait par inférence, ce qui signifie que l'essentiel des vérifications se fait de manière transparente, sans avoir à donner d'informations supplémentaire à OCaml, contrairement à ce qui se passe en Java ou en C# dans lequel il faut préciser le type de chaque variable, de chaque argument, chaque méthode... Là où les langages dynamiquement typés tels que Python ne contrôlent la cohérence des opérations que durant l'exécution du programme, OCaml analyse celle-ci intégralement lors de la phase de compilation, ce qui impose une rigueur supplémentaire lors de l'écriture du programme mais améliore la fiabilité et simplifie considérablement la phase de tests. De plus comme la vérification de type s'effectue à la compilation et non durant l'exécution, cette dernière n'est pas ralentie par des tests permanents de cohérence.
Par opposition à Java, C# ou Python, OCaml essaye le plus possible d'être un langage déclaratif, c'est-à-dire un langage dans lequel on va décrire la solution à un problème, plutôt que de construire cette solution étape par étape. Pour ce faire, OCaml est extensible, ce qui signifie que, lorsqu'une application a besoin d'une opération complexe et répétitive, il est possible d'ajouter cette opération sous la forme d'une nouvelle primitive du langage lui-même -- ou même d'ajouter à l'intérieur d'OCaml un langage de programmation spécialisé dans la résolution de votre problème. Ainsi, certaines bibliothèques, au lieu d'ajouter de nouvelles fonctionnalités au langage, modifient ce dernier.
Par opposition à Java, C# ou Python, OCaml essaye le plus possible d'être un langage de haute performance. Ainsi, un programme OCaml démarre beaucoup plus vite, s'exécutera fréquemment plus vite et consommera fréquemment 4 fois moins de mémoire vive qu'un programme Java ou C#. Un programme OCaml utilisera généralement un peu plus de mémoire vive qu'un programme Python mais s'exécutera souvent 10 fois plus vite. Dans certains cas, heureusement assez rares, ce gain de performances se fait au détriment du confort de programmation.
Ah, encore une chose : par opposition à Java ou C#, et tout comme Python, OCaml est et reste un langage d'expérimentation, ce qui signifie qu'il gagne régulièrement de nouvelles fonctionnalités inédites. Il existe ainsi des versions d'OCaml spécialisées dans la programmation distribuée (JoCaml, Acute), dans la manipulation d'arbres XML (OCamlDuce), dans l'écriture de compilateurs (MetaOCaml), dans l'écriture de scripts shell (Cash), etc.
Avec un peu d'expérience d'OCaml, vous constaterez qu'OCaml est un langage dans lequel les programmes écrits sont beaucoup plus concis qu'en Java ou C#, grâce à de puissantes primitives d'abstraction, grâce à l'extensibilité du langage, grâce à l'inférence de types et, tout simplement, grâce à une syntaxe plus concise. Cette concision est souvent un avantage, même si elle rend le code plus dense et donc parfois plus difficile à lire.
Pourquoi apprendre OCaml ?
Cette questions a plusieurs réponses.
Commençons par l'aspect technique et pécuniaire : OCaml est gratuit, tous les outils dont vous pouvez avoir besoin pour programmer sont gratuits (sauf l'ordinateur), toutes les extensions d'OCaml qui vous serviront lors de vos développements sont gratuites et même ce livre est gratuit.
Et en tant que langage, que m'offre OCaml ?
Pour les mathématiciens, physiciens, statisticiens ou autres utilisateurs de programmes scientifiques, OCaml permet de développer des programmes rapides et fiables, d'une part parce que le code source du programme ressemblera à la description mathématique du problème et sera donc plus simple à vérifier et d'autre part parce que les analyses permises par OCaml éviteront de nombreuses erreurs. De plus, la programmation fonctionnelle de haut niveau simplifie la constitution des bibliothèques génériques et aisément réutilisables.
Les programmeurs Java, C#, C, C++ ou autres utilisateurs de langages ou librairies système ou orientés-objets découvriront avec OCaml une autre manière de voir le monde et d'écrire des programmes dépendant moins du mode d'exécution de la machine et plus des résultats voulus, de modifier le langage de programmation pour l'adapter au problème au fur et à mesure de la progression du projet, ou encore d'autres techniques de factorisation et de réutilisation de code. Même si vous ne deviez pas utiliser OCaml dans votre carrière, les concepts vous permettront d'analyser un grand nombre de problème sous des angles que vous n'aviez pas prévus jusqu'à présent.
Les programmeurs Python, Ruby, JavaScript découvriront un langage confortable, dans lesquels ils retrouveront un grand nombre d'idées connues, sous une forme plus rigoureuse et plus adaptée au développement de grands projets critiques. À l'inverse, les utilisateurs de langages industriels -- dont l'essentiel des concepts datent des années 70 -- découvriront des idées plus récentes en provenance du monde de la Recherche. Pour information un grand nombre de concepts considérés comme novateurs dans l'industrie sont tirés directement des langages fonctionnels : les exceptions, la vérification de débordement dans les tableaux, le ramasse-miettes, l'inférence de types, les types génériques, les mini-langages spécialisés, les énumérations sûres, les clôtures...
Mise en bouche.
Avant d'entrer dans l'enseignement, commençons par une mise en bouche.
"Note:" Tout ce livre utilise OCaml Batteries Included, une distribution de OCaml qui étend aussi bien les bibliothèques standard que le langage de programmation lui-même. Cette bibliothèque est gratuite et disponible au téléchargement, mais il est important de souligner que le langage présenté ici n'est "pas" (encore) du OCaml standard, seulement l'une de ses variantes. 
Bonjour, le monde.
L'extrait suivant est un programme complet d'une ligne, qui a pour effet d'afficher le texte "Bonjour, le monde" :
print_endline ("Bonjour, le monde.")
On pourrait d'ailleurs simplifier légèrement le programme en supprimant les parenthèses, qui ne sont ici pas nécessaires, de manière à obtenir l'extrait suivant :
print_endline "Bonjour, le monde."
Dans ce qui précède, codice_1 est une fonction, dont l'effet est d'afficher du texte et de passer à la ligne, et qui n'a pas de résultat. En Java, ce serait l'équivalent de
public class Main
 public static final void main(String args[])
 System.out.println("Bonjour, le monde");
Pi.
Pour définir une valeur, il faut utiliser le mot clé codice_2, mot anglais que l'on peut traduire par le mot "soit" précédant les définitions en mathématiques.
Pour définir la valeur de pi, il suffit d'écrire : "soit pi = 3.1415926", c'est à dire :
let pi = 3.1415926
Cette ligne donne le nom pi à la valeur 3.1415926. Cette valeur ne changera plus au cours de l'exécution du programme. En Java, ce serait l'équivalent d'une déclaration :
public static final float PI = 3.1415926;
Identité.
Les fonctions sont des valeurs comme les autres. Ainsi, pour définir la fonction identité, qui à tout x associe x lui-même, on peut écrire
let id = fun x -> x
ou encore
let id(x) = x
ou encore
let id x = x
Ces trois définitions sont équivalentes. La première précise explicitement que id est une fonction. La troisième pourra être comprise comme la définition d'une famille de valeurs formula_1 pour tout formula_2 -- dans la dernière version, nous avons tout simplement supprimé les parenthèses qui étaient inutiles. Nous verrons plus tard que cette fonction va faire l'objet d'un certain nombre de vérifications à chaque fois qu'elle est appliquée. Comme précisé plus haut, ces vérifications ont entièrement lieu durant la compilation.
On utilise alors id de la manière suivante
let y = id 5;;
let z = id "Il fait beau";;
En Java, pour obtenir la même fonctionnalité, le plus simple est d'abandonner toute vérification et d'écrire une classe contenant la méthode suivante :
public class Fonctions
 public static Object id(Object x)
 return x;
On utilise alors id de la manière suivante :
int y = (Integer)Fonctions.id(5);
String z = (String)Fonctions.id("Il fait beau");
Si l'on cherche à conserver une vérification de types, il devient nécessaire de définir une classe plus complexe :
public class Id<T>
 public T appliquer(T x)
 return x;
On utilise alors id de la manière suivante :
int y = new Id<Integer>().appliquer(5);
String z = new Id<String>().appliquer("Il fait beau");
Profitons de cet exemple pour remarquer que, en OCaml, le mot-clé codice_3 n'existe pas et n'est pas nécessaire. En effet, le corps d'une fonction est une expression et toute expression est évaluée. En d'autres termes, le résultat d'une fonction est donné par la dernière expression de cette fonction.
Delta.
La fonction delta de Dirac est définie mathématiquement de l'ensemble des réels vers l'ensemble des réels par
Cette définition mathématique se traduit naturellement en OCaml par
let delta = function
 0.0 -> 1.0
 | _ -> 0.0
ou encore
let delta x = match x with
 0.0 -> 1.0
ou encore
let delta x = if x=0.0 then 1.0 else 0.0
Ces trois extraits se lisent "delta est la fonction qui à 0,0 associe 1,0 et qui à toute autre valeur associe 0,0". Retenez la structure des deux premiers extraits, qui reviendra fréquemment. Profitons-en pour préciser que le test d'égalité se note "=", qu'il fonctionne correctement, contrairement à celui de Java ou C# (dans lesquels il faut parfois utiliser "==" et parfois invoquer une méthode) ou Python (qui, par défaut, vérifie l'identité et non l'égalité).
Opérateurs logiques.
OCaml définit bien entendu les opérateurs logiques habituels. Nous pourrions, si nécessaire, les redéfinir à la main. Ainsi, le ou exclusif logique peut s'écrire
let ou_exclusif a b = match (a,b) with
 (true, true) -> false
Ce qui peut se résumer
let ou_exclusif a b = match (a,b) with
 (true, false) | (false, true) -> true
Dans ce qui précède, codice_4 et codice_5 sont les noms donnés en OCaml aux booléens vrai et faux. L'utilisation de codice_6 permet de prendre des décisions en fonction de la structure du couple (a,b).
Bien entendu, la même fonctionnalité pourrait s'écrire, de manière moins lisible, à l'aide des opérateurs logiques habituels :
let ou_exclusif a b = (a && not b) || (b && not a)
En Java, les premiers extraits se traduiraient
public static boolean ou_exclusif(boolean a, boolean b) 
 if ((a==true && b == false) || (a==false && b== true))
 return true;
 else
 return false;
Le dernier extrait s'écrirait
public static boolean ou_exclusif(boolean a, boolean b)
 return (a && !b ) || (!a && b );
On pourrait bien entendu, en OCaml comme en Java, écrire tout ceci à l'aide d'une comparaison entre codice_7 et codice_8, comme suit :
En Java :
public static boolean ou_exclusif(boolean a, boolean b)
 return (a != b);
En OCaml :
let ou_exclusif a b = a <> b
voire, de manière plus concise, 
let ou_exclusif = ( <> )
Remarquons que la fonction obtenue en OCaml est plus générique que la version Java, puisqu'elle permet de vérifier la différence entre n'importe quelle paire d'éléments du même type, pas uniquement des booléens.
Factorielle.
La fonction mathématique factorielle peut se définir de plusieurs manières. Directement, avec des points de suspension, on écrira formula_5. Ou encore sans points de suspension, par récurrence, à l'aide de
Ces deux définitions peuvent être utilisées en OCaml.
Définition récursive.
On peut traduire directement la deuxième définition par
let rec factorielle = function
Cet extrait se lit exactement comme la définition par récurrence.
Dans ce qui précède, codice_9 signifie que la fonction est définie par récursivité, mécanisme qui correspond à la définition mathématique par récurrence ou par induction : les cas non-triviaux sont déterminés à partir des cas précédents, ou encore la fonction s'appelle elle-même. En OCaml, la récursivité est la forme de boucle la plus fréquente.
On pourrait aussi écrire le code suivant, toujours récursif :
let rec factorielle n = if n >= 1 then n * ( factorielle (n - 1) ) else 1
En Java, on recommande d'écrire :
public static int factorielle(int n)
 int result = 1;
 for(int i = 1; i < n; ++i)
 result *= i;
 return result;
Il est aussi possible d'écrire une version récursive en Java, même si, à cause des limites des implantations actuelles de Java, ce genre de pratique est généralement évité :
public static int factorielle(int n)
 if(n<=0)
 return 1;
 else
 return n * factorielle (n-1);
Comme on peut le constater, les versions OCaml sont plus concises et plus proches de la définition mathématique que la version Java recommandée. Même si cela n'est pas fondamental pour un exemple aussi simple, on peut déjà constater qu'il y a un peu moins d'occasions de commettre des erreurs simples en suivant la structure OCaml que la structure Java recommandée. Cette tendance ne fait que s'amplifier pour des exemples plus complexes.
Définition directe.
On peut aussi définir le produit des nombres de 1 à n en opérant sur l"'énumération" des entiers de 1 à n, comme suit :
let factorielle n = reduce ( * ) ( 1 -- n )
Cette définition (légèrement fausse) se lit "factorielle n est le résultat qu'on obtient en appliquant la multiplication aux nombres consécutifs entre 1 et n". Pour obtenir cette définition, nous avons employé codice_10, c'est-à-dire l'énumération des entiers de 1 à n, l'opération codice_11, c'est-à-dire la multiplication, et la fonction codice_12, qui applique l'opération choisie aux deux premiers éléments de l'énumération, puis au troisième, puis au quatrième, etc. Si l'énumération ne contient qu'un seul élément, le résultat de codice_12 est cet élément, quelle que soit l'opération appliquée.
La notion d'énumération se retrouve dans d'autres langages de programmation, y compris Java ou Python, généralement sous le nom d"'itérateur". Il s'agit d'une manière d'accéder à tous les éléments d'un ensemble, dans un ordre donné, en ne consultant qu'une seule fois chaque élément. Cette notion est centrale en OCaml Batteries Included, puisqu'il s'agit de la manière principale de réaliser des boucles ou, plus généralement, des opérations répétitives sur une structure de données.
Pourquoi notre définition est-elle fausse ? Parce que nous n'avons pas correctement géré le cas où n<=0. Dans ces cas-là, l'énumération codice_10 est vide et la fonction que nous avons proposé n'est pas définie. Il convient donc de remplacer l'appel à codice_12 par un appel à une fonction légèrement plus complexe, codice_16, qui prend en plus une valeur initiale (ici 1) et est capable de traiter le cas de l'énumération vide :
let factorielle n = fold ( * ) 1 ( 2 -- n )
Cette définition de la factorielle renvoie 1 si codice_17 est inférieur ou égal à codice_18 et 1 * 2 * ... * n dans le cas contraire.
Manipulation de fichiers.
Un dernier exemple, qui fait cette fois appel à des bibliothèques de manipulation de fichiers. Le rôle de cet exemple est de lire et d'afficher le contenu d'un certain nombre de fichiers, en ajoutant avant chaque ligne le numéro de la ligne.
open Enum, File, IO;;
let add_line_number num line = (string_of_int num)^": "^line;;
iter f (args ())
where f arg = with_file_in arg (fun input ->
 write_lines stdout (mapi add_line_number (lines_of input))
La première ligne permet de faire appel à des fonctions prédéfinies et rangées dans les "module" codice_19 (tout ce qui a trait aux énumération), codice_20 (tout ce qui a trait aux fichiers) et codice_21 (des fonctions qui permettent de gérer la lecture ou l'écriture d'informations).
L'extrait définit ensuite une fonction codice_22 qui, à partir d'un nombre et d'une chaîne de caractères, construit une nouvelle chaîne de caractères qui commence par le nombre, puis continue par un caractère ":" et enfin le contenu de la chaîne initiale. Nous utiliserons cette fonction pour ajouter le numéro de la ligne avant le contenu de la ligne.
La ligne suivante fait appel à la fonction codice_23. Cette fonction, proche dans l'esprit de codice_12, permet de répéter un traitement et de l'appliquer à chacun des éléments d'une énumération. Ici, le traitement est donné sous la forme de la fonction codice_25, définie un peu plus loin, et l'énumération est codice_26, c'est-à-dire les arguments passés au programme par la ligne de commande -- si vous ignorez ce qu'est la ligne de commande, ce n'est pas important, sachez juste ici qu'il va s'agir de plusieurs noms de fichiers donnés au lancement du programme.
L'instruction codice_27 introduit une définition après son utilisation, ici la définition de la fonction codice_25. Cette fonction prend un argument codice_29, le nom du fichier à lire, puis invoque la fonction codice_30 avec cet argument codice_29 et avec une autre fonction codice_32. La fonction codice_30, définie dans le module codice_20, a pour rôle d'ouvrir un fichier (ici le fichier nommé codice_29), de passer le contenu de ce fichier à une autre fonction (ici, la fonction codice_36) et, une fois que cette dernière fonction a fini de s'exécuter, de fermer le fichier.
Que fait donc cette fameuse fonction codice_36 ? Partant d'un contenu de fichier codice_38, elle décompose ce contenu en une énumération de lignes à l'aide de codice_39, applique à chaque ligne consécutive la fonction codice_22 à l'aide de la fonction codice_41, puis écrit la nouvelle énumération ainsi obtenue dans le fichier codice_42, à raison d'une ligne à la fois, à l'aide de la fonction codice_43. Il aurait tout aussi bien été possible de décomposer en caractères à l'aide de codice_44, ou en nombre, ou en constructions plus complexes -- et de réécrire de même à l'aide de codice_45, etc. Quant à codice_42, il s'agit de la "sortie standard", c'est-à-dire non pas un fichier réel mais l'écran. En Java, ce serait l'équivalent de codice_47.
La fonction codice_30 constitue l'une des manières de gérer ce qui, en Java ou en Python, serait traité à l'aide d'un bloc de finalisation. En OCaml, la finalisation n'est pas (encore) une opération standard du langage mais peut être remplacée sans difficultés.
En Java, pour obtenir un résultat similaire, on aurait ajouté au programme quelque chose comme
import java.io.*;
LineNumberReader reader = new LineNumberReader(new FileReader("/tmp/in.txt"));
try
 try
 PrintWriter writer = new PrintWriter(new FileWriter("/tmp/out.txt"));
 reader.setLineNumber(1);
 for(String line = reader.readLine(); line != null; line = reader.readLine())
 writer.print(reader.getLineNumber+": "+line)
 } finally {
 writer.close();
} finally {
 reader.close();
//...
La complexité des deux programmes est comparable, même si l'on peut constater que, de nouveau, les bibliothèques de OCaml sont de plus haut niveau et 
donc plus simples à composer sans commettre d'erreurs. En particulier, dans la version Java, de nombreuses erreurs peuvent se glisser, qui ne seraient pas détectées par le compilateur, par exemple oublier de fermer les flux, oublier d'avancer progressivement dans un flux, tester si la ligne est vide au lieu de déterminer si elle vaut codice_49, utiliser codice_50 au lieu de codice_51, etc. À l'inverse, dans la version OCaml, le programmeur ne peut commettre aucune de ces erreurs.
Si nécessaire, OCaml fournit aussi les bibliothèques nécessaires pour gérer les fichiers à peu près de la même manière qu'en Java, ou pour traiter les expressions régulières.
Et maintenant ?
Si cette introduction vous a donné envie d'en apprendre plus, il est temps de voir Les bases d'OCaml.