On utilise les définitions de type pour modéliser des structures de données qui n'entrent pas facilement dans les structures de données de base prédéfinies en Caml. Une définition de type introduit toujours un nouveau type incompatible avec tous les types précédemment définis.
Les définitions de types sont toutes introduites par le mot-clé
type
. Les définitions de types sont récursives par défaut
et peuvent être polymorphes (en ce cas le type défini possède des
arguments (voir, ou)). Pour
définir simultanément plusieurs types on sépare les définitions par le
mot-clé and
.
Table des matières:
Il existe trois espèces de types de données définissables en Caml:
Un type somme est formé d'une liste de cas possible pour une valeur de ce type, chaque cas comporte un nom de cas, le ``constructeur'', et une (éventuelle) valeur associée (l'argument du constructeur).
Un cas dégénéré consiste à définir un type dont les constructeurs n'ont pas d'argument (constructeurs constants). On parle alors de type énuméré, type somme dont on enumère effectivement les constantes (le symbole ``|'' se lit ``ou''):
#type couleur = Bleu | Blanc | Rouge;; Le type couleur est défini. #Bleu;; - : couleur = Bleu
Les noms Bleu
Blanc
et Rouge
sont les constructeurs du type couleur
.
Dans le cas général les constructeurs ont des arguments: à la
définition du type somme on indique leur type après le mot-clef
of
.
Par exemple, on implémente les polynômes soit pleins (peu de zéros) soit creux
(beaucoup de zéros) par la définition du type polynôme à deux
constructeurs Creux et Plein:
#type polynôme = Creux of int list | Plein of int vect;; Le type polynôme est défini. #let nul = Creux [0];; nul : polynôme = Creux [0] #let un_plus_deux_x = Plein [|1; 2|];; un_plus_deux_x : polynôme = Plein [|1; 2|]
Les constructeurs permettent de distinguer les cas par filtrage:
#let minus_pol = function | Creux l -> Creux (map (function x -> - x) l) | Plein v -> Plein (map_vect (function x -> - x) v);; minus_pol : polynôme -> polynôme = <fun> #minus_pol nul;; - : polynôme = Creux [0] #minus_pol un_plus_deux_x;; - : polynôme = Plein [|-1; -2|]
Remarque: l'usage est d'écrire en majuscule la première lettre du nom d'un constructeur de valeur d'un type somme. Ainsi on choisit de nommer ``Creux'' plutôt que ``creux'' le constructeur des polynômes creux.
Pour un exemple de type somme polymorphe voir ici.
Un type enregistrement est formé d'une liste de rubriques comportant un nom de rubrique (le label) et une valeur associée (la valeur du champ):
#type personne = {Nom : string; Age : int};; Le type personne est défini. #let x = {Nom = "toto"; Age = 7};; x : personne = {Nom="toto"; Age=7} #x.Nom;; - : string = "toto"
Les rubriques d'un type enregistrement peuvent être déclarées mutables
lors de la définition du type. On a alors le loisir de les modifier
par la construction r.label <- nouvelle_valeur
.
Par exemple le solde d'un compte en banque est modifiable alors que le
numéro de ce compte est une donnée fixe:
#type compte = {Numéro : int; mutable Solde : float};; Le type compte est défini. #let compte_ref = ref 0;; compte_ref : int ref = ref 0 #let crée_compte dépôt_initial = incr compte_ref; {Numéro = !compte_ref; Solde = dépôt_initial};; crée_compte : float -> compte = <fun> #let compte_dupont = crée_compte 500.0;; compte_dupont : compte = {Numéro=1; Solde=500.0} #let dépose somme compte = compte.Solde <- compte.Solde +. somme;; dépose : float -> compte -> unit = <fun> #dépose 100.0 compte_dupont;; - : unit = () #compte_dupont;; - : compte = {Numéro=1; Solde=600.0}
Pour comprendre la différence entre un champ modifiable et un champ non modifiable contenant une valeur mutable voir ici.
Comme pour toutes les espèces de types Caml, on définit des types
enregistrements polymorphes en listant les arguments du nouveau
type.
Voici par exemple le type des cellules (ou arbres binaires)
polymorphes et mutables:
#type ('a, 'b) cell = {mutable Car : 'a; mutable Cdr : 'b};; Le type cell est défini. #{Car = 1; Cdr = "ok"};; - : (int, string) cell = {Car=1; Cdr="ok"} #let rplaca cell val = cell.Car <- val;; rplaca : ('a, 'b) cell -> 'a -> unit = <fun>
On peut aussi définir des types sommes polymorphes:
#type ('a, 'b) sexpr = Symbol of string | Cell of ('a, 'b) cell;; #let nil = Symbol "";; nil : ('a, 'b) sexpr = Symbol "" #let cons v1 v2 = Cell {Car = v1; Cdr = v2};; cons : 'a -> 'b -> ('a, 'b) sexpr = <fun> #let l = cons 1 (cons 2 nil);; l : (int, (int, ('_a, '_b) sexpr) sexpr) sexpr = Cell {Car=1; Cdr=Cell {Car=2; Cdr=Symbol ""}}
Un type abréviation définit un alias pour une expression de type:
#type compteur == int;; Le type compteur est défini. #let x = (1 : compteur);; x : compteur = 1
Les valeurs d'un type abréviation peuvent être considérées comme du type abrégé:
#x + 1;; - : int = 2
Les types abréviation servent dans les modules (pour définir un type exigé par l'interface du module) ou à des fins de documentation.
Contacter l'auteur Pierre.Weis@inria.fr