Cours 2

Htmligure
Version & licenses
Creative Commons License

Autres types paramétrés

Guyslain
Thursday, 21 July 2016

Retour au sommaire.

Nous venons de détailler trois catégories de types paramétrés :

  • les conteneurs, qui stockent des ensembles de valeurs du type du paramètre,
  • les producteurs, qui permettent de construire des valeurs du type du paramètre,
  • les consommateurs, qui produisent un résultat en consommant des valeurs du type du paramètre.

Ces catégories ne se veulent ni exhaustives (certains types paramétrés ne correspondent pas à une catégorie), ni nécessairement disjointes. Par exemple, nous avons dit que le type des fonctions 'elt -> 'im = ('elt,'im) funct est paramétré, mais selon qu'on se focalise sur le paramètre 'elt ou 'im, est un consommateur ou un producteur.

Nous avons aussi pu constater des similitudes dans l'utilisation des différents types, notamment l'utilisation de combinateurs permettant de construire des instances de types paramétrés avec comme paramètre le type que nous souhaitons.

Ces mécanismes sont typiques de la programmation fonctionnelle. L'existence des combinateurs est fortement facilité par le fait que la représentation interne des types que nous venons d'étudier est une représentation fonctionnelle : tous ces types sont en fait des types de fonctions spécialisés (il suffit de vérifier les définitions de ces types dans QuickCheck). Ceci n'est possible qu'en programmation fonctionnelle, au moins de façon naturelle. On peut bien sûr aussi le faire par exemple en Java, en utilisant des interfaces, mais ce sera moins naturel et plus verbeux.

Au-delà de ces remarques concernant l'aspect fonctionnel des interfaces décrites, retenons que les types paramétrés sont omniprésents en OCaml. Les comprendre, c'est pouvoir comprendre les librairies de l'écosystème. Ce cours doit vous donner les moyens de lire, comprendre et utiliser une API Ocaml.

Terminons par QuickCheck. Nous avons vu comment écrire un générateur d'instances, avec un générateur aléatoire, une fonction de conversion en chaîne de caractères et une fonction de réduction des instances. Une fois obtenu une valeur de type 'instance arbitrary, il faut construire le test lui-même en utilisant le prédicat à tester. C'est la fonction QCheck.Test.make qui le permet :

  1. val Test.make :
  2. ?count:int (* optional: number of tests to be performed (default is 100) *)
  3. -> ?name:string (* optional: name of test, to display on failure *)
  4. -> 'a QCheck.arbitrary (* instance generator *)
  5. -> ('a -> bool) (* predicate that should be always true *)
  6. -> Test.t (* The result: a unit test *)

Ensuite, il suffit d'exécuter le test avec la fonction :

  1. val Test.check_exn : Test.t -> unit

Cette fonctionne retourne rien. En cas de test échoué, elle lance une exception avec la description de l'instance faisant échouer le test. En résumé, la démarche pour créer un test unitaire est :

  1. créer un générateur aléatoire avec QCheck.Gen,
  2. créer une fonction de conversion avec QCheck.Print,
  3. créer une fonction de réduction avec QCheck.Iter,
  4. créer des prédicats à tester,
  5. créer un test avec QCheck.Test.make, pour chaque prédicat,
  6. exécuter chaque test avec QCheck.Test.check_exn,
  7. corriger les erreurs et recommencer.

Retour au sommaire.