Xalg / Fix / Lire un refus
Navigation
Apprendre
Recettes
Référence
Comprendre
Bonnes pratiques
En pratique
Explications

Lire un refus

Fix refuse tôt et souvent — c'est son contrat : ce qui compile termine et est bien typé, donc ce qui ne peut pas être garanti est refusé avant de tourner. Un refus n'est pas une panne : c'est une information précise. Cette page apprend à la lire — chaque message ci-dessous est reproduit tel quel.

Et chaque refus routé se termine par un pointeur vers sa fiche — → :help termination — la doc arrive au moment exact où tu en as besoin, jamais ailleurs (elle ne s'invite pas sur un succès).


Les familles de refus

Type — la donnée ou la règle contredit le typage

fix: fait mal typé : `etat(auth, retabli)`
    position 1 : `retabli` n'est pas une valeur du type `statut`.
    Corrige : ajoute cette valeur au type, ou utilise une valeur déjà déclarée du type.
    → :help types

fix: règle mal typée : `add` — à la position 1 de `add`, une règle produit une étiquette là où le type déclaré attend un nombre
    → :help types

Le fait mal typé : une valeur n'appartient pas au type déclaré (retabli n'est pas un statut). La vérification descend dans les structures — l'intrus est trouvé même au fond d'une liste. La règle mal typée : une opération exige ce que le kind inféré du type ne permet pas (un choix de valeurs nommées passé à add) — tu n'as rien déclaré, le garde-fou a travaillé seul. → Les kinds.

Terminaison — la règle pourrait ne pas s'arrêter

fix: terminaison non prouvable : règle croissante essai (position 0)
    → :help termination

La règle fabrique des valeurs neuves (par add, ou en construisant un terme en tête) et se nourrit d'elle-même. Le vérificateur ignore les gardes — il refuse aussi des programmes qui terminent. Réécriture : forme décroissante, ou objets nommés. → La terminaison.

Sûreté — une variable de tête sortie de nulle part

fix: règle non sûre : longueur (variable de tête non liée, L₀)
    → :help safety

Toute variable de tête doit être liée par un littéral relationnel du corps — un fait, un prédicat — pas seulement testée par une garde ou laissée libre. → Bonnes pratiques.

Stratification — négation ou agrégat en circuit fermé

fix: cycle négatif (programme non stratifiable) : p
    → :help negation

fix: agrégat non-idempotent dans un cycle : v
    → :help aggregation

not et agg lisent leur source complète ; une définition circulaire à travers eux n'a pas de valeur stable. → La négation, L'agrégation.

Modules — la frontière tient

prédicat non exporté : infra::depend/2
    → :help modules

module non importé : infra::impacte/2
    → :help modules

Ce que le module n'exporte pas n'existe pas dehors ; ce que tu n'as pas importé non plus. Et un nom exporté par plusieurs modules importés doit s'utiliser qualifié (val/1 importé de plusieurs modules : m1, m2). → Les modules.

Déclaration désactivée

fix: déclaration désactivée (pull-driven, cf. backlog) : dual
    → :help ilp

dual et generalize sont volontairement hors service (leur réalisation provisoire cassait des garanties) — la déclaration est refusée plutôt que silencieusement ignorée.

Syntaxe

fix: `type paire(a, …)` : un lieur lie des VARIABLES, et une variable de type s'écrit en MAJUSCULE — écris `paire(A)`.
    → :help syntax

Un paramètre de type est une variable, et une variable s'écrit en majuscule — partout (type list(T) = nil | cons(T, list(T)).). La casse porte le sens : paire(T) accepte n'importe quel type, paire(t) désigne LE type nommé t.

Évaluation incomplète

fix: évaluation incomplète : point fixe non atteint en 100000 itérations
    → :help termination

La saturation n'a pas convergé — soit une accumulation récursive sur données cycliques (préfère la forme relaxation, voir Recettes), soit un cycle décroissant sous un agrégat récursif. Le signal arrive en temps borné, le résultat partiel n'est jamais rendu comme complet.


Le réflexe : où le refus pointe

Le message nomme toujours l'objet fautif — le fait tel que tu l'as écrit, le prédicat, la position d'argument. Trois questions dans l'ordre :

  1. Un fait est cité ? La valeur contredit un type ou un sig — regarde la déclaration correspondante, l'un des deux a tort.
  2. Un prédicat de règle est cité ? Regarde ses variables de tête (toutes liées par un littéral relationnel ?) puis son flux de valeurs (fabrique-t-il du neuf en boucle ?).
  3. Un cycle est cité ? Suis la boucle nommée : quelque part, un not ou un agrégat se lit lui-même.

Les messages sont des données

Le catalogue des gabarits est chargé dans la base au démarrage — interrogeable et extensible comme le reste :

?- error_template(type_error, fr, T).
T = le prédicat {} attend {}, reçu {}

?- error_severity(type_error, S).
S = fatal

Et comme c'est une relation, elle s'énumère — la liste complète des refus possibles est une requête, pas une annexe à maintenir :

?- error_template(C, fr, T).
C = type_error, T = le prédicat {} attend {}, reçu {}
C = type_error_fact, T = fait mal typé : {}
...        % ~50 gabarits

Ajouter une langue est un :assert, pas une recompilation — voir Recettes.


Les bords honnêtes