Files
saselang-bible/chapters/022-collections-et-iteration.md
2026-09-12 08:56:27 +02:00

221 lines
8.7 KiB
Markdown

# 22. Collections et itération
## 22.1 `Array<T>` — V1 REQUIS — DIRECTION FIGÉE
`Array<T>` est une séquence contiguë dont la taille est fixée lors de la création et n'est pas redimensionnable.
## 22.2 `StaticArray<T,N>` — V1 REQUIS — DIRECTION FIGÉE
`StaticArray<T,N>` est un tableau à taille connue à la compilation.
Le mécanisme exact de const generics nécessaire à `N` doit être défini avec les generics.
## 22.3 Collection redimensionnable — V1 REQUIS — À FINALISER
Un type du type :
```text
Vector<T>
```
est envisagé pour les séquences redimensionnables, mais l'API et le nom final doivent être figés dans le SDK V1.
## 22.4 `Iterable<T>` / `Iterator<T>` — V1 REQUIS — FIGÉ
Interfaces Core reconnues par `foreach`.
Elles ne portent pas le préfixe `Op`, car `foreach` est une construction du langage et non un opérateur symbolique.
Indexabilité et itérabilité sont indépendantes.
## 22.5 `Range<T>` — V1 REQUIS — FIGÉ EN PRINCIPE
`Range<T>` est une vraie valeur Saselang immuable représentant un intervalle ordonné. Il ne représente ni un pas ni une progression d'itération.
Les quatre syntaxes de bornes sont :
```text
a..b [a, b] bornes basse et haute incluses
a>..b (a, b] borne basse exclue, borne haute incluse
a..<b [a, b) borne basse incluse, borne haute exclue
a>..<b (a, b) bornes basse et haute exclues
```
Les deux bornes doivent avoir exactement le même type `T`. Saselang ne possède pas de `Range<L,U>` et ne recherche aucun type commun implicite entre deux bornes déjà typées différemment.
Un littéral non encore typé peut toutefois être contextualisé normalement :
```text
Range<uint64> ids = 1..100;
```
### 22.5.1 Domaine ordonné
`Range<T>` est disponible lorsque les valeurs admissibles utilisées comme bornes appartiennent à un domaine totalement ordonné selon le contrat de comparaison retenu par Saselang.
La règle est fondée sur la capacité du type, pas sur une whitelist fermée. Elle peut donc s'appliquer à des entiers, `char`, des enums ou structs explicitement ordonnés, et à des types Core/utilisateur satisfaisant le contrat requis.
Les enums n'obtiennent pas automatiquement un ordre à partir de leur ordre de déclaration : ils doivent fournir explicitement la capacité d'ordre total requise.
Les floats sont autorisés comme bornes de `Range<floatN>` uniquement pour leur sous-domaine fini :
```text
NaN interdit comme borne
+Infinity interdit comme borne
-Infinity interdit comme borne
valeur finie autorisée
```
`-0.0` et `+0.0` occupent la même position dans l'ordre numérique d'un range, même si leur représentation binaire reste distincte et observable par les mécanismes bas niveau appropriés.
`NaN` et les infinities n'appartiennent à aucun `Range<floatN>` défini avec ces règles ; `contains()` retourne donc `false` pour ces valeurs.
### 22.5.2 Construction et ranges vides
Les bornes sont évaluées de gauche à droite, exactement une fois chacune, avant la construction du range.
Une borne statiquement prouvée invalide provoque une erreur de compilation. Une borne calculée dynamiquement mais invalide selon le contrat de `Range<T>` provoque un runtime fault lors de la construction directe. Une API Core explicite retournant `Result` pourra être fournie lorsqu'une validation récupérable est souhaitée.
`lower > upper` ne constitue pas une erreur : le résultat est un range vide.
Lorsque les bornes sont égales :
```text
a..a contient exactement a
a>..a vide
a..<a vide
a>..<a vide
```
Un range vide est une valeur valide. En revanche, un range statiquement vide utilisé comme pattern de `match` est interdit parce que la branche serait inatteignable.
Les ranges ouverts/infinis tels que `..b`, `a..` ou `..` ne font pas partie de V1.
### 22.5.3 API fondamentale
`Range<T>` doit exposer au minimum les capacités conceptuelles suivantes, dont les noms sont retenus sauf raison ultérieure forte de les changer :
```text
range::lower()
range::upper()
range::includesLower()
range::includesUpper()
range::isEmpty()
range::contains(value)
```
Le range est immuable : modifier une borne signifie construire un autre `Range<T>`.
Le `match` utilisant un range-pattern emploie la même sémantique d'appartenance sans invoquer un prédicat utilisateur arbitraire.
## 22.6 Progression et itération des ranges — V1 REQUIS — FIGÉ EN PRINCIPE
L'existence d'un `Range<T>`, son itérabilité et sa progression sont trois concepts distincts :
```text
Range<T> intervalle ordonné
RangeIterable<T> parcours canonique vers l'avant
RangeStep<T,Step> parcours avant avec pas explicite
RangeReverse<T> parcours canonique vers l'arrière
RangeReverseStep<T,Step> parcours arrière avec pas explicite
RangeProgression<T,Step> valeur de progression produite
```
Ces noms sont retenus comme noms Core significatifs ; leur namespace exact sera décidé lors de l'organisation générale de Core, probablement dans une famille dédiée aux ranges.
Un type n'est pas obligé de fournir toutes ces capacités. Un range peut être valide sans être itérable ; il peut être itérable vers l'avant sans supporter `reverse()` ou `step()`.
`foreach` continue à consommer `Iterable<T>` : un `Range<T>` ou une `RangeProgression<T,Step>` n'est utilisable par `foreach` que lorsque les capacités Core concernées fournissent cette itérabilité.
### 22.6.1 Progression canonique
Lorsqu'un type fournit `RangeIterable<T>`, le parcours direct d'un range utilise sa progression canonique. Celle-ci n'est pas définie génériquement comme un appel caché à `step(1)`.
Pour les entiers Core, la progression canonique avance naturellement d'une unité. Pour `char`, elle avance parmi les Unicode scalar values valides et saute les valeurs surrogate `U+D800..U+DFFF`, qui ne sont pas des `char` Saselang valides.
Un `Range<floatN>` est un intervalle valide lorsque ses bornes sont finies, mais il n'est pas automatiquement itérable et ne reçoit pas automatiquement une capacité de `step` flottant.
### 22.6.2 `step(...)`
Le pas ne fait jamais partie du `Range`. La syntaxe de parcours explicite est :
```text
(range)::step(step)
```
Elle produit une progression itérable et ne modifie pas le `Range` d'origine.
La compatibilité entre le type des bornes `T` et le type du pas `Step` est définie explicitement par `RangeStep<T,Step>` ou `RangeReverseStep<T,Step>`. Elle n'est pas déduite automatiquement d'une conversion numérique générale.
Cela permet notamment de définir des couples adaptés au domaine :
```text
RangeStep<int32,int32>
RangeStep<int32,uint32>
RangeStep<char,uint32>
RangeStep<Date,Duration>
```
et de refuser des couples qui introduiraient une perte ou n'auraient pas de sens. Par exemple, un range `float16` peut recevoir seulement les types de pas explicitement supportés sans imposer qu'un `float32` plus large soit accepté.
Le pas exprime une magnitude de progression, pas une direction. Pour les types numériques Core, il doit être strictement supérieur à zéro. Un pas statiquement nul ou négatif est une erreur de compilation ; une valeur dynamique invalide provoque un runtime fault lors de la construction directe de la progression. Une API explicite en `Result` pourra exister pour une validation récupérable.
Pour les types utilisateur, la validité du pas est définie par le contrat Range correspondant et n'est pas réduite artificiellement à une comparaison numérique avec zéro.
### 22.6.3 `reverse()`
La direction inverse est explicite :
```text
(range)::reverse()
(range)::reverse()::step(step)
```
`reverse()` ne construit pas un range aux bornes inversées. Le `Range` reste identique ; seule la direction de parcours change.
La forme canonique compose la direction avant le pas :
```text
range
[::reverse()]
[::step(step)]
```
Une progression déjà matérialisée par `step()` n'est pas requise de fournir à son tour `reverse()`. Cela évite plusieurs chaînes d'appels synonymes pour le même parcours.
L'inclusion ou l'exclusion des bornes reste une propriété du range et ne change jamais avec la direction.
### 22.6.4 Arrêt, overflow et pas non aligné
Une progression s'arrête avant de produire une valeur hors du range. Elle ne doit jamais provoquer un overflow uniquement pour constater qu'elle a atteint la fin.
Par exemple :
```text
(250u8..255u8)::step(2u8)
```
produit conceptuellement :
```text
250 252 254
```
sans tenter de former `256`.
De même :
```text
(1..10)::step(4)
```
produit :
```text
1 5 9
```
La borne finale n'est pas ajoutée artificiellement si elle ne tombe pas naturellement sur la progression.
Un range vide produit simplement une progression vide.