Files
saselang-bible/chapters/034-packages-identite-et-collisions-de-namespaces.md
2026-09-12 08:56:27 +02:00

165 lines
5.2 KiB
Markdown

# 34. Packages, identité et collisions de namespaces
## 34.1 Identité indépendante du namespace — V1 REQUIS — FIGÉ
L'identité d'un package est indépendante de ses namespaces internes.
Identité canonique :
```text
vendor/package
```
Coordonnée exacte :
```text
vendor/package@version
```
Sélecteur de dépendance :
```text
vendor/package@requirement
```
Exemples :
```text
saselang/core@1.4.0
saselang/compiler-core@^0.8
acme/http-client@>=2.5,<3.0
sdl/sdl@3.1.*
```
Le `vendor/package` désigne l'identité logique. La version exacte ou l'exigence SemVer ne change jamais cette identité ; elle sélectionne une instance/résolution de cette identité.
Le namespace interne peut être totalement différent et ne doit pas être dérivé automatiquement de `vendor/package`.
## 34.2 Syntaxe des identifiants package — V1 REQUIS — FIGÉE EN PRINCIPE
Pour `vendor` et `package` :
```regex
[a-z][a-z0-9]*(?:-[a-z0-9]+)*
```
Règles :
- ASCII lowercase ;
- `-` sépare les mots à l'intérieur d'un composant ;
- `_` est interdit ;
- aucune conversion automatique `-` <-> `_` n'existe ;
- `/` sépare `vendor` et `package` ;
- `@` introduit une version exacte ou un requirement SemVer.
Cette convention doit fonctionner de manière identique dans les manifests, le resolver, le lockfile, les outils et la qualification `from` des imports.
## 34.3 Collision de namespaces — V1 REQUIS — FIGÉ
Deux packages peuvent légalement définir le même namespace et le même nom de symbole sans représenter le même type.
Par exemple, deux packages peuvent chacun fournir :
```text
namespace parser.ast;
public class Node
```
L'identité sémantique réelle d'un symbole inclut au minimum :
```text
instance de package résolue
+ namespace
+ symbole
```
Ainsi :
```text
acme/parser@2.4.1 :: parser.ast.Node
other/parser@5.0.0 :: parser.ast.Node
```
sont deux types distincts.
## 34.4 Qualification d'import par dépendance — V1 REQUIS — FIGÉ
Tout symbole provenant d'une dépendance externe doit être importé explicitement avec une clause `from` contenant son sélecteur canonique :
```text
from "vendor/package@requirement"
```
Direction syntaxique :
```text
import func some.namespace::function from "sdl/sdl@2.1.*" as sdlfun1;
import type some.namespace.Type from "acme/parser@^3.0" as ParserType;
```
La grammaire finale des différentes formes `import type|func|const|var` reste à harmoniser avec la grammaire lexicale complète, mais les règles sémantiques suivantes sont figées :
- `from` est **obligatoire** pour toute provenance extérieure au package courant ;
- le sélecteur `vendor/package@requirement` doit correspondre à un requirement effectivement déclaré dans le scope de dépendances applicable au fichier/package ;
- `from` ne déclenche jamais une résolution ad hoc ;
- le lockfile associe ce requirement à une version exacte résolue ;
- les dépendances transitives ne sont pas directement importables : un package qui utilise directement leur API doit les déclarer comme dépendances directes ;
- après l'import, le code utilise le nom local du symbole ou son alias ; la coordonnée package n'apparaît pas dans les expressions ordinaires.
Sans `from`, un `import` ne peut viser que le package courant.
L'alias `as` reste facultatif lorsque le nom local obtenu est unique dans le fichier. Il devient nécessaire lorsque plusieurs imports produiraient le même nom local ou lorsqu'un renommage explicite est souhaité.
## 34.5 Plusieurs versions directes d'une même identité — V1 REQUIS — FIGÉ EN PRINCIPE
Un même package consommateur peut déclarer plusieurs requirements directs pour le même `vendor/package` afin d'utiliser plusieurs lignées incompatibles en parallèle.
Exemple conceptuel :
```text
sdl/sdl@2.1.*
sdl/sdl@^3.0
```
Le code choisit explicitement l'instance souhaitée avec `from` :
```text
import func ... from "sdl/sdl@2.1.*" as sdlfun1;
import func ... from "sdl/sdl@^3.0" as sdlfun2;
```
Si les deux imports exposent le même nom terminal dans le même fichier, des alias locaux distincts sont obligatoires. La sélection de version ne peut pas être reportée à un chemin pleinement qualifié utilisé directement dans le corps du code.
Pour un même scope effectif de dépendances, deux requirements portant sur le même `vendor/package` doivent être **disjoints**.
Exemples autorisés :
```text
sdl/sdl@2.1.*
sdl/sdl@^3.0
```
Exemples interdits :
```text
sdl/sdl@>=1.0
sdl/sdl@^4.5
```
car les ensembles de versions acceptées se recouvrent.
De même :
```text
sdl/sdl@^3.0
sdl/sdl@3.1.*
```
est interdit puisque `3.1.*` appartient déjà à `^3.0`.
Le resolver doit détecter l'intersection SemVer des requirements avant résolution. Une intersection non vide est une erreur de manifest, même si une version particulière permettrait techniquement de satisfaire les deux requirements.
Cette règle supprime toute ambiguïté entre plusieurs slots directs de la même identité et rend `from "vendor/package@requirement"` déterministe.
Le graphe transitif peut lui aussi contenir plusieurs versions d'une même identité. Chaque instance résolue conserve une identité de type distincte lorsque les versions ne sont pas unifiées par le resolver.