Menu1 Menu2 Menu3 Menu4 Menu5 Menu6

Sommaire
Qu'est-ce qu'un type ?
Les types de base
   Les entiers
   Les réels
   Les booléens
   Les tableaux
   Les listes
   Différences entre tableau et liste
   Les caractères
   Les chaînes de caractères
   Les références
Les types composés
De nouveaux types
Polymorphisme
Changements de type
Un type à part : unit

I) Qu'est-ce qu'un type ?
Les objets Caml sont typés, c'est-à-dire qu'ils ont une certaine nature, qu'ils sont classés dans certaines catégories. Ainsi, une fonction définie pour agir sur un entier (qu'elle soit prédéfinie ou que vous l'ayez écrite) ne pourra agir sur des listes par exemple. De même, lorsque vous définissez un objet, toute modification de cet objet devra se faire en conservant le type. Exemple : on définit une référence entière (voir plus bas) a valant 0 :
let a = ref 0;;
Caml vous répond en disant que a est du type « int ref ». Si vous voulez modifier a, en lui affectant la valeur 8 (en tapant a := 8;;), cela ne pose aucun problème, le type de a reste « int ref ». En revanche, si vous donnez à a une valeur flottante comme 0.5 (en tapant a:= 0.5;;), vous déclenchez une erreur : le type n'est pas bon et Caml affiche : « This expression has type float but is here used with type int ».
De même, la fonction prédéfinie float_of_int (convertit un entier en flottant. Ex : 2 devient 2.0) est du type « int -> float » : elle prend un entier en argument et rend un flottant. Du coup, si vous l'appelez avec un flottant (essayez float_of_int 6.0;;, Caml répond encore « This expression has type float, but is used with type int ». Vous remarquerez d'ailleurs un point fort de Caml : au lieu de simplement déclencher une erreur, il vous indique d'où vient l'erreur en soulignant avec des ^^^ le passage qui lui pose problème. Lorsqu'on écrit des programmes longs, c'est pratique pour vite voir l'erreur. « This expression » désigne le passage souligné, soit ici « 6.0 ». La réponse de Caml signifie que ce que vous avez tapé (6.0) est du type float (c'est vrai !) mais que vous l'avez utilisé dans un contexte qui demande un entier.

II) Les types de base
1) Les entiers (type int)

C'est l'objet de base de tout calcul. On notera que les entiers de Caml sont en fait les relatifs en mathématiques : -8 est pour Caml un entier. (En fait, OCaml ne connaît pas tous les entiers, pour la simple raison que certains sont trop gros pour être mis en mémoire. Pour la plupart des processeurs, les entiers Caml vont de -2^30 à 2^30-1, ce qui laisse quand même une très bonne marge ! Plus précisément, Caml raisonne par modulo : l'entier qui vient après 2^30-1 est ... -2^30 !). Les opérations sur les entiers sont les suivantes :
- Addition : pour ajouter a et b entiers, on tape simplement a + b.
- Soustraction et multiplication : tout simplement a - b et a*b.
- Division : un problème se pose déjà. 4/2 donne 2, c'est bon. Mais que dire de 5/2 ? Doit-on rendre 2.5, qui est alors un flottant ? Les concepteurs de Caml ont choisi de rester dans les entiers : 5/2 doit donner un entier. Dès lors, quand vous tapez a/b, vous ne réalisez pas vraiment la division de a par b, mais vous obtenez le quotient de la division euclidienne de a par b : 5/2 donne 2 (5 = 2 x 2 + 1) et 17/3 donne 5 (17 = 5 x 3 + 2).
- L'opérateur de division va toujours avec son copain : l'opérateur mod. Celui-ci rend le reste dans la division euclidienne (et donc un entier) : 17 mod 5 donne 2, 5 mod 2 donne 1.
- successeur et prédécesseur : la fonction succ ajoute 1 à un entier. La fonction pred enlève 1.
- inversion : pour avoir l'opposé de a, il suffit de taper -a, qui reste de type int.
- la mise en puissance d'un nombre (exemple, 2^3 = 8) n'existe pas, vous devez définir une fonction puissance si vous en avez besoin. De même, la racine carrée n'agit que sur des réels et n'est pas définie sur des entiers.
- les fonctions min et max donnent le minimum et le maximum de deux entiers.

2) Les réels (type float)
Les nombres flottants sont les nombres avec partie décimale. Notez bien qu'ils sont représentés à l'anglaise (comme toujours en informatique) : le point remplace la virgule (qui a d'ailleurs une signification précise en Caml, voir plus bas) : 8.7 est un flottant, 9. en est un autre. Les opérations sur les flottants sont presque les mêmes que sur les entiers :
- L'addition, la soustraction et la multiplication ont le même effet que sur les entiers. Mais pour les différencier, on ajoute aux opérateurs un point : on les note +. -. *. (pour ajouter 2. et 8., tapez 2. +. 8.;; sans oublier un seul point !).
- On peut toujours prendre l'opposé d'un flottant, mais on note là aussi avec un point : -.
- En revanche, la division (notée, vous vous en doutez, avec un point : /.) est ici la division habituelle : 5. /. 2.;; donne 2.5.
- La racine carrée existe, on la calcule par la fonction sqrt.
- De même, la mise en puissance existe : a**b donne ab.
- La trigonométrie existe en Caml. On dispose de cos, sin, tan, acos, asin, atan. Par contre, la valeur de pi n'est pas prédéfinie. Si vous en avez besoin, il faut la définir par : let pi = 4. *. (atan 1.);; (ou tout autre calcul de votre choix, mais on prend généralement celui-ci car il est simple)
- La fonction exponentielle existe : exp. La fonction ln existe mais attention : elle est nommé en Caml log, ne confondez pas avec le logarithme en base 10 ! (en cas de doute, vérifiez : log (exp 1.);; rend bien 1)

3) Les booléens (type bool)
Dans un langage quelconque, quand vous effectuez un test (par exemple si vous testez si a vaut 2), vous calculez en fait la valeur d'un booléen. Un booléen est une valeur logique et peut prendre deux valeurs : true et false. Opérations sur les booléens :
- le et logique est défini de deux manières en Caml : vous pouvez taper & ou &&. La différence ? Elle est simple : le symbole && est un opérateur dit paresseux : il ne fait pas toujours les deux évaluations. En clair, si vous tapez a && b, le résultat est true lorsque a et b valent true. Caml calcule donc la valeur de a. S'il se rend compte que a est faux, peu importe la valeur de b : le résultat sera forcément false. Ainsi, il existe une valeur de a pour laquelle Caml donnera le résultat a && b sans calculer b : c'est un opérateur paresseux. En revanche, l'opérateur & évalue toujours les deux valeurs a et b.
- le ou logique : là aussi, il y a deux syntaxes en Caml. Vous pouvez utiliser l'opérateur or ou l'opérateur || (chaque barre est obtenue avec les touches « Alt Gr » et « 6 » sur le pavé alphabétique). La distinction est la même : || est un opérateur paresseux, pas or.
- la négation existe, on utilise l'opérateur not.
- les tests : pour effectuer deux opérations différentes selon qu'un test est vrai ou faux, on utilise « if - then - else » : if [mettre ici une condition, qui a la forme d'un booléen] then [mettre ici le résultat de la fonction si le test est vrai] else [mettre ici le résultat sinon]. On peut bien entendu distinguer plus de deux cas : par exemple, écrivons une fonction qui prend un entier en argument. Si celui-ci vaut 1, elle rend le texte « un », si l'entier vaut 2, elle rend « deux », et sinon, elle rend le texte « aucun des deux » :
let f x =
if x = 1
   then "un"
   else if x = 2
      then "deux"
      else "aucun des deux";;

- Le ou exclusif n'est pas défini en Caml. On peut le créer ainsi :
let ouex a b = if a = true then (if b = true then false else true)
                                   else (if b = true then true else false);;

Toutefois, ce programme est très maladroit : on fait des tests sur des valeurs booléennes, ce qui est absurde ! Prenons le premier test : « if a = true ». Autrement dit, on prend la valeur de a et on voit si elle vaut true. Finalement, le résultat de « a = true » vaut justement a (si vous n'êtes pas convaincu, essayez les deux valeurs de a). On peut donc écrire ce test ainsi : if a.
De même, « if b = true then false else true » peut être écrit « if b then false else true ». Mais on peut encore simplifier, car cela revient à donner la valeur non b. D'où une meilleure syntaxe : « not b ». De même, la dernière parenthèse se traduit tout simplement par « b ». D'où une meilleure fonction :
let ouex a b = if a then not b else b;;
Pensez à ce genre de simplifications quand vous créez des fonctions sur les booléens ou que vous faites des tests sur des booléens !
Note historique : les booléens sont nommés ainsi dans pratiquement tous les langages en hommage au mathématicien anglais George Boole qui a créé la logique moderne, également appelée algèbre de Boole.

4) Les tableaux (types array)
Ce sont des ensembles d'objets numérotés de type quelconque. Par contre, tous les objets d'un même tableau doivent être du même type. Pour la syntaxe, on note un tableau ainsi : il commence par [|, puis on inscrit les éléments séparés par des points-virgules et on termine par |]. Par exemple, pour signifier le tableau d'entiers contenant 1 et 2, on écrit : [|1;2|]. Caml indique alors que ce tableau est du type « int array » (à lire à l'envers : array composé d'int). On peut tout à fait définir un tableau vide : [||]. L'avantage des tableaux est que ses éléments sont numérotés. On peut donc accéder directement à un élément en connaissant son numéro. Mais attention ! L'indexation des tableaux Caml commence à 0 : le premier élément a pour numéro 0. Du coup, si vous avez un tableau à 37 éléments, les numéros vont de 0 à 36. Opérations sur les tableaux :
- Calcul de la longueur : Array.length donne la longueur d'un tableau (nombre d'éléments).
- Accès à un élément : la fonction Array.get permet en tapant Array.get t i de lire l'élément numéro i, d'un tableau t. On peut aussi taper t.(i)
- Modification : la fonction Array.set permet en tapant Array.set t i a de placer l'objet a en position i dans le tableau t. On peut aussi utiliser la syntaxe t.(i) <- a
- Extraction s'un sous tableau : vous avez un tableau t à n éléments. Pour en extraire un tableau plus petit on utilise Array.sub. Il faut taper Array.sub t i n où t est le tableau de départ, i est le numéro à partir duquel commence le nouveau tableau, n est la longueur du nouveau tableau. Par exemple, Array.sub [|1;2;3;4;5|] 1 2donne [|2;3|].
- Création d'un tableau à n éléments a : si vous voulez définir un tableau contenant 100 fois le nombre 0, vous pouvez éviter d'avoir à taper cent zéros et 99 point-virgules. Pour cela, utilisez la fonction Array.make : Array.make 100 0. Vous pouvez aussi utiliser la fonction Array.create qui est équivalente mais dépréciée.

5) Les listes (type list)
Ce sont aussi des ensembles d'objets de même type, mais non numérotés. L'avantage des listes est qu'on peut modifier leur longueur (c'est impossible avec un tableau : un tableau de longueur 1 le reste). La syntaxe est la même que celle des tableaux en supprimant les barres verticales à côté des crochets : la liste contenant 1 et 2 s'écrit [1;2]. En revanche, on ne peut accéder dans une liste qu'au premier élément.
- Calcul de la longueur : comme pour un tableau, on utilise ici List.length.
- Accès au premier élément de la liste l (appelé tête de l) : on tape juste List.hd l
- Pour avoir le reste de la liste l, c'est-à-dire l privé de sa tête, on tape List.tl l. On obtient évidemment une liste plus courte. Si la liste l était déjà vide, on obtient une erreur.
- Pour concaténer des listes : si vous voulez concaténer les listes a1, a2, ..., an, c'est-à-dire créer une nouvelle liste contenant d'abord les éléments de a1 puis ceux de a2, puis ... puis ceux de an, utilisez List.concat [a1 ; a2 ; ... an]. Vous vous interrogez peut-être : j'ai mis toutes mes listes dans une liste !? Bah oui, une liste peut contenir n'importe quel type d'éléments donc pourquoi pas des listes ?
Si vous n'avez que deux listes, vous pouvez écrire plus rapidement a1@a2. Par exemple, [1;2]@[3;4;5] donne [1;2;3;4;5].
- Pour ajouter un élément seul au début d'une liste, on utilise :: (deux fois deux points) : 5::[1;2] donne [5;1;2]. En fait, on pourrait aussi taper [5]@[1;2].
- On peut renverser une liste avec la fonction List.rev (de l'anglais reverse). En associant avec List.hd, on peut lire le dernier élément d'une liste.

6) Différences entre tableau et liste :
Le tableau a l'avantage de permettre l'accès direct à tout élément avec son numéro mais a l'inconvénient d'avoir une taille fixe. Inversement, la liste a une taille modulable indéfiniment mais on n'accède qu'à son premier élément.
En fait, on peut comparer le tableau à une étagère et la liste à une pile de papiers : dans une étagère, vous ne rajoutez pas de rayon, mais vous pouvez accéder à chaque rayon. Dans une pile de papier, vous pouvez empiler des papiers, enlever des papiers, mais vous ne voyez toujours que le papier au sommet.
Du fait de ces différences, la structure de tableau est plus adaptée à la programmation impérative, la structure de liste à la programmation récursive (voir cours suivant)

7) Les caractères (type char)
Il en existe 256. Ce sont tous les caractères existant en informatique : aussi bien des nombres que des lettres (on différencie majuscule et minuscule) ou des signes de ponctuation, voire le caractère ENTREE ou EFFACER. On les note en OCaml entre deux apostrophes (touche 4 du clavier alphabétique) : par exemple 'z'. Pour ce qui est des caractères spéciaux, comme justement l'apostrophe, il n'est pas question de la taper directement. En effet, si vous vouliez le faire, vous devriez tapez 3 apostrophes successives, ce que Caml ne pourrait pas interpréter car l'apostrophe est réservée pour entourer des caractères ! Les caractères réservés sont donc précédés d'un antislash. Par exemple, le caractère « apostrophe » est noté '\''. Du coup, l'antislash lui-même est aussi un caractère spécial : pour l'obtenir, tapez '\\'.
En fait, quand je dis qu'il y a 256 caractères, il faut être prudent. En effet, en 1961 fut inventé le code ASCII (American Standard Code for Information Interchange), un code qui fait correspondre à chaque nombre de 0 à 127 un caractère (il y en a donc 128). Le code ASCII a permi de normaliser ces caractères et les rendre quasiment universel (si votre ordinateur ne les connaît pas, c'est que son constructeur est un tordu). Caml se sert de ce code pour représenter ses caractères. Mais il en manque 128 !
Oui, car le code ASCII est américain. Il ne contient pas nos caractères accentuées, ni les caractères tildés espagnols, ni l'Eszet allemand, ni les lettres cyrilliques, ni.... Pour pallier à cela, on a rajouté au code ASCII 128 caractères (d'où les 128 + 128 = 256 de Caml) numérotés de 129 à 255. Là, le problème est que ce code (nommé ASCII étendu) répond à des problèmes locaux ou nationaux et n'est pas internationalement réglementé. Ainsi, ces 128 caractères peuvent être différents d'un ordinateur à un autre, selon son pays voire dans un même pays selon sa marque ! Je recommande donc d'utiliser ces caractères directement dans vos programmes sans passer par leur numéro qui risquerait de changer entre deux ordinateurs (voir plus bas pour la correspondance caractère/numéro en Caml).

8) Les chaînes de caractères (type string)
Ce sont des successions de caractères. On les note en mettant au début et à la fin des guillemets hauts (touche 3 du pavé alphabétique), par exemple "Le chat a mangé". En fait une chaîne de caractères peut être comparée à un tableau de caractères, car on réalise les mêmes opérations :
- Calcul de la longueur avec String.length.
- Accès au caractère numéro i de la chaîne s : on utilise la fonction String.get avec les mêmes arguments que Array.get ou on tape s.[i]. (attention : ici, on met des crochets). Comme pour les tableaux, l'indexation commence à 0.
- Modification du caractère i : String.set (semblable à Array.set) ou s.[i] <- '?'.
- Extraction d'une sous-chaîne : String.sub, les arguments sont les mêmes que Array.sub.
- Création d'une chaîne : String.make, les arguments sont les mêmes que Array.make.
- On dispose d'une fonction supplémentaire, empruntée aux listes : String.concat sep [s1 ; ... ; sn] rend la chaîne composée par la succession des chaînes s1 à sn, séparées par sep. Par exemple, String.concat "/" ["03";"09";"2006"] donne "03/09/2006". Pour concaténer deux chaînes seulement, on peut aussi utiliser ^ entre les deux.
La chaîne vide existe : ""

9) Les références (type ref)
On a vu dans le cours précédent que Caml n'est pas dynamique : si vous définissez un entier a valant 5, vous ne pourrez plus modifier sa valeur. On dispose pour cela d'un nouveau type : la référence. Si vous tapez let c = ref 0;;, vous obtenez un objet dont le type est « int ref » et dont vous pouvez modifier la valeur. Pour cela, on utilise l'opérateur := (deux points suivis de égal). En tapant c := 5;;, vous affectez à la référence c la valeur 5. Désormais, tout appel à la valeur de c rendra 5, jusqu'à modification. Et pour accéder à la valeur de c (si vous en avez besoin dans une fonction demandant un argument du type int), il suffit de taper !c.
Bien sûr, vous pouvez mettre n'importe quel type d'objet dans une référence : une liste, un couple, une fonction, ... Cependant, il existe deux opérations réservées aux références entières : les fonctions incr (de « incrémentation ») et decr (« décrémentation ») permettent d'augmenter ou diminuer de 1 la valeur d'une référence.

III) Les types composés
1) n-uplets
Vous pouvez définir des n-uplets d'objets. L'avantage par rapport aux listes et aux tableaux est que les objets peuvent être de types différents. Un n-uplet est écrit en séparant les différents éléments par des virgules : par exemple 2,5,"test" est un triplet du type « int*int*string » et 5,true,2.5,'y' est un quadruplet de type «int*bool*float*char».
Particularité pour les couples : on peut accéder aux deux éléments grâce aux fonctions fst (de l'anglais first) et snd (second) : fst (2,5) donne 2 et snd (2,5) donne 5.
A propos des listes : la fonction List.split s'applique à une liste de couples. Elle rend deux listes, la première composée des premiers éléments de chaque couples, la seconde composée des seconds éléments de chaque couple. (Par exemple, List.split [(1,2);(3,4)] rend ([1;3],[2;4])). Inversement, la fonction List.combine reçoit deux listes et rend la liste composée des couples (List.combine [1;3] [2;4];; rend [(1,2);(3,4)]). Si les deux listes sont de longueurs différentes, on a une erreur (plus précisément une exception, voir le cours Gestion des exceptions).
List.assoc a l où l est une liste de couples cherche un couple dans l dont le premier élément est a, et rend l'autre élément de ce couple. La fonction List.mem_assoc permet au préalable de savoir si un tel couple existe.

2) Composition
Les tableaux, listes, références, n-uplets, peuvent contenir n'importe quel type d'objets, et donc des listes, des tableaux, etc ! Ainsi on peut définir des objets de type très complexe : par exemple, [[|1|], true], ref [2] est du type « (int array * bool) list * int list ref ». Cela semble farfelu, mais il arrive qu'on utilise des types complexes. Par exemple, j'utilise dans un de mes programmes (démineur), des objets de type (int*int*bool) list.

IV) De nouveaux types
Si vous utilisez des types de ce genre dans un programme, la lecture peut devenir très difficile. Ne serait-ce que si vous définissez une fonction prenant en argument trois objets de ce genre et en rendant un du même type, vous aurez un fonction dont le type est : « (int array * bool) list * int list ref -> (int array * bool) list * int list ref -> (int array * bool) list * int list ref -> (int array * bool) list * int list ref ». C'est totalement illisible ! Il peut donc être utile de définir un nouveau type d'objets, pour simplifier. Par exemple, si votre type « (int array * bool) list * int list ref » correspond à une manière de coder une carte météo, vous pouvez définir le type météo comme étant égal à « (int array * bool) list * int list ref ». Ainsi, votre fonction sera du type « météo -> météo -> météo -> météo ». Beaucoup mieux ! Pour définir ce type, on procède ainsi :
Exceptionnellement, on ne commence pas la définition par let mais par type. En revanche, le reste est classique : on donne le nom du type, un signe égal, la définition de l'objet, puis deux points-virgules :
type meteo = (int array * bool) list * int list ref;;.
Puis quand vous définissez votre fameuse fonction, vous allez faire ce qu'on appelle un "forçage de type" : vous allez lui imposer un type pour les arguments. Au lieu de taper let f x y z = ....;;, vous entrez let f (x:meteo) (y:meteo) (z:meteo) = .... ;; en veillant aussi à préciser que le résultat final est du type météo (par exemple : let resultat:meteo = ... in ... ;;. Et alors, votre fonction est bien du type avec les 4 météos !

V) Polymorphisme
J'introduisais ce cours en disant qu'une fonction doit s'appliquer à un objet de type prédéfini. Ce n'est pas toujours vrai : par exemple, l'opérateur = peut servir à comparer des entiers, mais aussi des flottants, des booléens, des listes, ... Il peut comparer n'importe quels objets, tant qu'ils sont du même type. On dit alors que cet opérateur est polymorphe.
Autre exemple : on a vu la fonction Array.make. Elle prend en argument un entier et un objet quelconque, qui servira à remplir le tableau créé. Si vous demandez son type à Caml, il vous répond « int -> 'a -> 'a array ». Le symbole 'a indique que le deuxième argument peut être de type quelconque. Caml vous indique aussi que le résultat est du type 'a array : c'est un tableau rempli d'éléments dont le type est quelconque, mais est le même que le deuxième argument : si vous tapez Array.make 2 [2], vous obtiendrez un résultat de type int list array (car [2] est du type int list).
Les opérateurs > (strictement plus grand), >= (plus grand ou égal), < (strictement plus petit) et <= (plus petit ou égal) sont également polymorphes mais ne s'appliquent pas à tous les types : ils comparent des entiers, des flottants ou des caractères mais pas des listes.
Tapez let l = ref [];;. Caml va y reconnaître une référence de liste. Cependant, si vous aviez donné [1] ou ['2'], il aurait su de quel type est cette liste mais là elle est vide. Il ne sait pas ce qu'on peut mettre dedans. Sa réponse sur le type est « '_a list ref ». Cela signifie-t-il que la liste est de type quelconque ? Non, car il aurait écrit « 'a list ref ». Il y a pour votre liste un trait en plus : cela signifie que cette liste a un type précis mais que Caml ne connaît pas encore. Si vous tapez plus tard let test () = List.hd (!l) = 2;;, Caml va comprendre que List.hd (!l) est un entier (car vous le comparez à un entier) et donc que la liste dans la référence l est une liste d'entiers. A partir de là, le type de l deviendra « int list ref » et le restera !

VI) Changements de type
Voyons les diverses fonctions prédéfinies dans Caml pour passer d'un type à un autre :
- la fonction Array.to_list (de type 'a array -> 'a list) transforme un tableau en la liste contenant les mêmes éléments. La fonction inverse est Array.of_list.
- la fonction float_of_int (de type int -> float) transforme un entier en un flottant équivalent (par exemple 1 devient 1.0). On peut s'en servir pour définir la racine carrée d'un entier : let racine x = sqrt (float_of_int x);;
- la fonction int_of_float n'est pas tout à fait la fonction inverse. En effet, il n'existe pas d'entier équivalent à 1.5. En fait, la fonction int_of_float correspond à la partie entière : int_of_float 1.5 donne 1.
- int_of_char et char_of_int établissent l'équivalence entre un caractère et son numéro dans le code ASCII (ou ASCII étendu si besoin).
- int_of_string existe. On ne doit l'appliquer qu'avec une chaîne de caractères ne comprenant que des chiffres (ou éventuellement un signe moins au début). On obtient alors l'entier correspondant. La fonction inverse est string_of_int.
- Les fonctions float_of_string et string_of_float font la même chose avec des flottants.
- string_of_bool et bool_of_string associent les booléens et leurs représentations en mots (anglais).

VII) Un type à part : unit (effets de bord)
Ce type est assez difficile à décrire. En fait, il n'existe qu'un seul élément de ce type, et on se moque de sa valeur. La fonction Array.set par exemple est du type 'a array -> int -> 'a -> unit : elle prend quelques arguments et en retour effectue une action (actualiser un tableau) mais ne rend rien sinon la valeur : (). C'est ce qu'on appelle un effet de bord : une chose qui se passe (cela pourrait aussi être tracer un trait sur l'écran, voir le cours sur le graphisme) mais qui ne rend pas de résultat.
On peut enchaîner les effets de bord. Dans ce cas, on les sépare par des points-virgules. Par exemple, une fonction peut inclure le code t.(0) <- 5 ; t.(1) <- 8, ce qui aura pour effet de changer deux éléments de t. De plus, une fois ces actions effectuées vous pouvez faire un calcul et rendre son résultat. Par exemple, vous avez défini la référence c tout à l'heure. Créons une fonction qui reçoit un entier, affecte à c cet entier et rend son double : let test x = c:= x ; 2*x;;. Cette fonction est bien du type int -> int, mais elle modifie au passage la valeur de c, ce qui est un effet de bord. Et si on ne rendait pas le double de c, c'est-à-dire si on se contentait d'affecter x à c, test serait du type int -> unit.
Il existe également des fonctions qui ne prennent aucun argument. Par exemple, créons la fonction qui nous donne la valeur actuelle de c : let actuel() = !c;;. Vous voyez qu'on a remplacé dans cette syntaxe l'argument de la fonction par des parenthèses, qui indiquent à Caml qu'il n'y a pas d'argument à cette fonction. Elle est de type unit -> int
Bref, la compréhension de ce type n'est pas aisée. Ca viendra après avoir vu des exemples.

> Haut de la page