Variants

Résumé des types que l'on connait

Avant d'aller plus loin, et de découvrir une des fonctionalité les plus importantes d'OCaml, il est utile de faire une pause et de résumer les types que l'on connait.

Nous avons vu les types "de base":

  • bool pour les booléens: "vrai" ou "faux".
  • int pour les entiers: 420, 2, ...
  • float pour les nombres à virgule: 4.14, 2.0, 3.33334, ...
  • char pour les caractères: a, \n, #
  • string pour les chaînes de caractères: "salut tout le monde", ...

Mais aussi des types pour combiner des types en des types nouveaux:

  • Les types fonction: par exemple, à partir du type int et du type float, on a le type fonction int -> float.
  • Le type des listes : à partir d'un type, on peut créer le type des listes d'éléments de ce type. Par exemple, avec le type float, on peut créer le type des listes de nombres à virgule : float list.

Est-ce que ça suffit ? Non ! Pourquoi ?

OCaml permet de créer des types personnalisés pour représenter ce que l'on veut ! Par exemple on pourra créer des types pour :

  • Un personnage de jeu de rôle,
  • Une recette de cuisine,
  • Une carte à jouer,
  • Un dictionaire,
  • ...

Mais avant de créer ces types, nous allons nous concentrer sur les types que l'on connait déjà, et voir comment ils ont été défini ! Et comment nous aurions pu les définir nous-même !

Anatomie d'un booléen

OCaml nous permet de jeter un oeil à la définition d'un type:

#show bool

et le résultat ne se fait pas attendre!

type bool = false | true

Mais qu'est-ce que cela peut bien vouloir dire ?

  • "type" ?
  • "bool" ?
  • "=" ?
  • "false" ?
  • "|" ?
  • "true" ?
  • type nous indique que l'on va déclarer un type, et non autre chose (une valeur, un module, une pâte à tartiner, ...)
  • bool est le nom du type,
  • false et true sont les deux valeurs que peuvent prendre le type,
  • | sert à séparer les différentes valeurs possibles.

On appelle ça un type énuméré : un type dont la définition énumère les différentes valeurs possibles.

Création d'un type énuméré

Essayons tout d'abord de recréer le type booléen :

type mon_bool = true | false

Est-ce que ça marche ? Oui !

Alors, mettons-le en français :

type mon_bool = vrai | faux

Est-ce que ça marche ? Non !

Pour éviter de "confondre" les "valeurs de base" des valeurs normales, on force à utiliser des majuscules pour les nouvelles valeurs de base.

On appelle les "valeurs de base" définie lors de la création d'un type des "constructeurs". Rappelez-vous ce terme ! Ils sont toujours en majuscule.

type mon_bool = Vrai | Faux

Mais plutôt que de reprendre un type existant, ce qui n'est pas très utile, créens-on un nouveau !

Du tricot en OCaml

type maille = Endroit | Envers

tricot

Note : Il n'y a que deux possibilités, comme pour les booléens ! On aurait pu utiliser des booléens pour représenter ces valeurs, mais cela ne pourrait que nous mélanger les aiguilles, et générer des bugs !

Nous pouvons maintenant filtrer et utiliser ces valeurs:

let esthetique maille =
  match maille with
   | Endroit -> 1.3
   | Envers -> 1.2

let retournement maille =
  match maille with
   | Endroit -> Envers
   | Envers -> Endroit

Augmentons un peu le type...

type maille =
  | Endroit
  | Envers
  | Glisse (** Passe une maille d'un côté à l'autre sans la tricoter *)
  | Jete (** "Jette" une maille *)
  | Augmente (** Crée deux mailles à partir d'une seule *)

Horreur ! Le filtrage n'est plus exhaustif ! Corrigeons cela tout de suite !

Le fait que OCaml soit capable d'emettre un warning est un très bon point pour la maintenance des projets.

Dans certains cas, on peut se permettre d'avoir un "cas joker":

let connu_de_paul_elliot maille =
  match maille with
   | Endroit -> true
   | Envers -> true
   | _ -> false

Même si on rajoute un nouveau genre de point, Paul-Elliot ne le connaitra pas pour autant.

Exercices sur les variants énumérés

Des constructeurs avec données

Les constructeurs permettent de créer des valeurs incompatibles. Cependant:

  • Par moment, ce n'est pas très pratique de tout énumérer
  • On peut en vouloir un nombre infini !

On peut ajouter des données à un constructeur.

type temperature_dimensionnee =
  | Celsius of float
  | Farenheit of float
let temp_corporelle = Celsius 37.

let en_farenheit t = match t with
  | Farenheit f -> f
  | Celsius c -> (c *. 9.) /. 5. +. 32.
type json =
  | Null
  | Bool of bool
  | Int of int
  | Float of float
  | String of string
  | Array of json list
  | Object of (string * json) list;;

Exercices sur les Variants avec données

Enregistrements

On peut combiner des valeurs en une seule en utilisant des enregistrements. La syntaxe est la suivante :

type ethique = Loyal | E_Neutre | Chaotique

type moral = Bon | M_Neutre | Mauvais

type personnage_dd = {
  nom : string
  ethique : ethique;
  moral : moral;
}
let p = { nom = "Monsieur L" ; ethique = Loyal ; moral = Bon}
let nom_de p = p.nom
let est_pire p = match p with
  | {nom = _ ; ethique = Chaotique ; moral = Mauvais} -> true
  | _ -> false

Exercices sur les enregistrements

Quelques types classiques

  • Le type option permet de définir une valeur qui peut être présente, ou absente :
type 'a option = 
  | None   (** La valeur est absente *)
  | Some of 'a  (** La valeur est présente *)
  • Le type list permet de définir une liste de valeurs :
type 'a list = 
  | []   (** La liste est vide *)
  | ( :: ) of 'a * 'a ilst  (** Un élément en tête, et le reste de la liste *)
  • Le type either permet de définir l'union de deux types :
type ('a, 'b) either = 
  | Left of 'a   (** Un élément de type 'a *)
  | Right of 'b  (** Un élément de type 'b *)

Finir les exercices !