Les modules : des espaces de noms, structurels
Au-delà du petit programme, tu veux organiser, isoler, réutiliser. Les modules font ça — sans système de modules posé à côté du langage.
Déclarer, exporter, appeler
module(infra, [impacte/2]).
depend(web, api).
depend(api, db).
impacte(S, T) :- depend(S, T).
impacte(S, T) :- impacte(S, U), impacte(U, T).
end_module.
use_module(infra).
% appel qualifié, dans une règle
alerte(S) :- infra::impacte(S, db).?- alerte(X).
X = web
X = api
?- infra::impacte(web, X).
X = api
X = db
?- infra::depend(web, X).
prédicat non exporté : infra::depend/2
→ :help modules
module(nom, [exports])ouvre un module et déclare ce qui sort.end_modulele ferme.nom::predicatappelle un prédicat qualifié — dans un corps de règle comme en requête directe au REPL ; aprèsuse_module, la forme non qualifiée marche aussi.depend/2n'est pas exporté : de l'extérieur, il n'existe pas — et Fix te le dit.
Hiérarchiques et étanches
Les modules s'organisent en chemins — lib::maths — mais la hiérarchie se déclare à plat : le chemin est le nom du module, on n'emboîte pas les blocs (un module(…) dans un module(…) est refusé : module imbriqué interdit).
module(lib::maths, [carre/2]).
carre(2, 4).
carre(3, 9).
end_module.
use_module(lib::maths).
verif(X) :- lib::maths::carre(3, X).?- verif(X).
X = 9
?- lib::maths::carre(2, X).
X = 4
Et l'encapsulation est absolue : de l'extérieur, seuls les prédicats exportés existent. Le reste est invisible, pas seulement « par convention » — Fix le fait respecter.
Deux modules, un même nom : qualifie
Deux modules peuvent exporter le même prédicat — ils coexistent tant que chaque usage est qualifié :
module(m1, [val/1]).
val(un).
end_module.
module(m2, [val/1]).
val(deux).
end_module.
use_module(m1).
use_module(m2).
copie(X) :- m1::val(X). % ✓ qualifié : sans ambiguïté
C'est l'usage non qualifié d'un nom ambigu qui est refusé, à la compilation :
copie(X) :- val(X). % ✗ lequel ?fix: val/1 importé de plusieurs modules : m1, m2
Pas de règle de priorité, pas de dernier-importé-gagne : l'ambiguïté est une erreur, la résolution t'appartient.
Minimaliste, par doctrine
Pas de type classes, pas de foncteurs façon ML, pas de signatures séparées, pas de dispatch dynamique. Ce n'est pas un manque : c'est un choix. Tout ça est soit encodable avec ce qu'il y a déjà (modules hiérarchiques + règles de types TLLP/KLLP), soit contraire à l'esprit déclaratif.
Le module reste ce qu'il doit être : un espace de noms avec une frontière.
Pas une couche en plus
Un nom qualifié, c'est une structure — et Fix résout les structures pour vivre. Les modules ne demandent donc aucun moteur dédié : c'est le même geste, organisé.
Les bords honnêtes
- Volontairement minimaliste : pas de foncteurs, pas de sealing paramétré, pas d'emboîtement physique des blocs (la hiérarchie vit dans le nom).
- L'encapsulation passe par l'export explicite : ce que tu ne listes pas ne sort pas.
À côté
- Les types comme programme (TLLP) — de quoi remplacer ce qu'on attendrait des type classes.
- Référence : modules — la forme exacte.