Les consommateurs sont des types paramétrés 'consumed t qui se comporte comme des fonctions prenant des valeurs de type 'consumed en argument. Tout l'intérêt est d'obtenir des résultats, à chaque application ou bien de façon différée, donc les consommateurs permettent de récupérer des valeurs dépendant des valeurs consommées. Le type des valeurs consommés est donc le paramètre du type paramétrés du consommateur.
Un premier exemple est le type 'value QCheck.Print.t qui n'est qu'un renommage du type 'value -> string. Ce sont donc des fonctions consommant des valeurs selon leur type paramétré, et fournissant des chaînes de caractères.
Une fois de plus QCheck.Print définit des combinateurs, afin de créer des fonctions de conversions plus intéressantes :
Par ailleurs, si on sait convertir un entier en chaîne de caractères, et qu'on sait obtenir une entier depuis une fraction, on sait convertir une fraction en chaîne de caractères : il suffit d'obtenir un entier depuis la fraction et de convertir cet entier. Nous avons déjà vu ce mécanisme, avec les fonctions map dans QCheck.Iter et dans QCheck.Gen ! Sauf que si on y fait attention, cette fois-ci les types sont dans l'autre sens :
D'une certaine façon, les producteurs et les consommateurs fonctionnent en miroir les uns des autres, ce n'est donc pas étonnant que les types des fonctions map et comap soient aussi miroir. En tout cas nous pouvons maintenant écrire un fonction pour afficher le dénominateur d'une fraction :
ou pour afficher le premier dénominateur d'une liste non-vide de fractions :
Il existe de nombreux exemples de consommateurs. En voici un autre pour encoder des prédicats, c'est-à-dire des fonctions produisants des booléens.
Une valeur de type value Predicate.t est essentiellement une fonction du type value vers le type bool. On peut d'ailleurs créer un Predicate.t à partir d'une fonction value -> bool en utilisant la fonction Predicate.checks. Et on peut utiliser un prédicat pour tester une valeur avec Predicate.test.
Voyons quelques exemples :
is_even est un prédicat sur les entiers, décidant la parité. En utilisant le combinateur Predicate.all, on construit un prédicat sur les listes d'entiers, qui décide si tous les entiers d'une liste sont pairs.
Tout comme Print, Predicate propose un combinateur comap. Le prédicat se lit "tous les dénominateurs sont égaux à 1" , il décide donc si toutes les fractions d'une liste sont entières. Notons l'usage de @@ qui permet d'écrire le code dans le sens de lecture naturel, alors que dans les producteurs on utiliser l'application inversée |>. C'est de nouveau l'effet miroir entre producteurs et consommateurs.
On termine avec un prédicat pour tester si une liste d'associations, c'est-à-dire de paires (clé, valeur), possède une certaine clé. On peut utiliser comap pour récupérer la clé d'une paire, ou bien faire un prédicat sur les paires avec both.