This commit is contained in:
2026-09-13 21:59:15 +02:00
parent b72193d656
commit 6e0a4a91d9
19 changed files with 434 additions and 151 deletions

View File

@@ -125,6 +125,29 @@ part[0]::getName(); // OK
Le backing storage nécessaire à une slice reste vivant aussi longtemps que nécessaire. Cette garantie ne requiert aucune syntaxe de lifetime utilisateur.
Lorsqu'une slice provient d'un `Vector<T>`, elle référence un backing storage stable/indirect et non une adresse physique brute dont la validité dépendrait d'une allocation précise. Une relocalisation physique du buffer est donc invisible sémantiquement.
En V1 :
```text
remplacement d'un élément du Vector
reserve(...) / relocalisation physique
append(...)
-> les Slice existantes restent valides
insert(...)
removeAt(...)
clear() ayant effectivement supprimé au moins un élément
-> toutes les Slice dérivées de ce Vector sont invalidées
utilisation ultérieure d'une Slice invalidée
-> SliceInvalidatedFault
```
`append` préserve les slices existantes parce qu'il ne change ni la position ni l'identité logique des éléments déjà présents. `insert` et `removeAt` peuvent déplacer les positions existantes ; V1 choisit donc une invalidation globale simple plutôt qu'un suivi dynamique de toutes les plages actives.
Un `clear()` sur un vector déjà vide n'effectue aucune mutation structurelle et n'introduit pas une invalidation supplémentaire.
Une slice vide est une valeur valide.
Les indices d'une sous-slice sont relatifs à la slice source.
@@ -274,19 +297,29 @@ list[index] = value;
### 22.4.6 `ResizableList<T>`
`ResizableList<T>` étend `SettableList<T>` et garantit des opérations structurelles capables de modifier la longueur.
Les signatures exactes seront figées avec `Vector<T>`.
Un `resize(newLength)` sans information d'initialisation n'est pas retenu implicitement : agrandir une collection doit définir comment les nouveaux éléments sont construits.
Lorsqu'il existe, `clear()` suit la convention :
`ResizableList<T>` étend `SettableList<T>` et garantit les opérations structurelles fondamentales suivantes :
```text
clear() -> uint64
interface ResizableList<T> extends SettableList<T> {
method append(T value) -> Void;
method insert(uint64 index, T value) -> Void
faults IndexOutOfBoundsFault;
method removeAt(uint64 index) -> T
faults IndexOutOfBoundsFault;
method clear() -> uint64;
}
```
et retourne le nombre d'éléments effectivement supprimés.
`insert(index,value)` accepte `0 <= index <= length()`. `index == length()` est donc un append positionnel valide, même si `append(value)` reste la forme explicite recommandée pour cette intention.
`removeAt(index)` exige `0 <= index < length()` et retourne l'élément retiré. Le modèle futur copy/move fixe son transfert physique sans changer cette signature sémantique.
Un `resize(newLength)` sans information d'initialisation n'est pas retenu : agrandir une collection ne doit jamais inventer implicitement des valeurs zéro, nulles ou non initialisées.
`clear()` retourne le nombre d'éléments effectivement supprimés.
### 22.4.7 Capacité du type et permission `const`
@@ -325,11 +358,80 @@ Vector<T>
ResizableList<T>
```
`Vector<T>` reste à définir précisément.
Un type persistant/immutable par nature n'a aucune obligation d'implémenter `List<T>` s'il ne garantit pas sa sémantique.
### 22.4.9 `Set<T>` et `ResizableSet<T>`
### 22.4.9 `Vector<T>` — V1 REQUIS — FIGÉ
`Vector<T>` est l'implémentation standard SDK d'une séquence contiguë redimensionnable. Il implémente `ResizableList<T>`.
Surface fondamentale :
```text
Vector<T>::empty() -> Vector<T>
Vector<T>::filled(uint64 length, T value) -> Vector<T>
Vector<T>::generate(...) -> Vector<T> // signature finale avec les callables
Vector<T>::builder() -> VectorBuilder<T>
length() -> uint64
count() -> uint64
isEmpty() -> bool
vector[index] -> T
vector[index] = value
append(T value) -> Void
insert(uint64 index, T value) -> Void
faults IndexOutOfBoundsFault
removeAt(uint64 index) -> T
faults IndexOutOfBoundsFault
clear() -> uint64
capacity() -> uint64
reserve(uint64 additional) -> Void
slice(Range<uint64> range) -> Slice<T>
```
`capacity()` et `reserve()` sont propres à `Vector<T>` et ne font pas partie de `ResizableList<T>`.
`reserve(additional)` garantit, si l'opération réussit, une capacité suffisante pour ajouter au moins `additional` éléments supplémentaires sans nouvelle croissance de capacité. Il ne change jamais `length()`.
Le contrat standard ne fournit pas :
```text
reserveExact()
resize(newLength) sans valeur/factory
shrink général
remove(value) générique
```
Une collection spécialisée peut exposer une opération de shrink lorsqu'un besoin réel le justifie ; cette capacité n'appartient ni à `Vector<T>` général ni à `ResizableList<T>`.
Les littéraux `[...]` restent les littéraux contextualisés d'`Array<T>` / `StaticArray<T,N>` et ne construisent pas implicitement un `Vector<T>` :
```text
Vector<int32> values = [1, 2, 3]; // ERROR
```
La construction multi-éléments dynamique passe par une class method/factory explicite, notamment le builder.
`Vector<T>` garantit :
```text
indexation [] / []= O(1)
length / count / isEmpty O(1)
append O(1) amorti
insert O(n) au pire
removeAt O(n) au pire
slice O(1)
capacity O(1)
reserve O(n) au pire si relocalisation nécessaire
```
Ces garanties appartiennent au type concret `Vector<T>`, pas à `List<T>` ou `ResizableList<T>` en général.
### 22.4.10 `Set<T>` et `ResizableSet<T>`
`Set<T>` étend `Collection<T>`.
@@ -368,7 +470,7 @@ clear()
Il n'existe pas de niveau `SettableSet<T>` : remplacer un élément d'un set équivaut conceptuellement à une modification structurelle de son ensemble de valeurs.
### 22.4.10 `Map<K,V>`, `SettableMap<K,V>` et `ResizableMap<K,V>`
### 22.4.11 `Map<K,V>`, `SettableMap<K,V>` et `ResizableMap<K,V>`
`Map<K,V>` n'étend pas `Collection<T>` et n'implémente pas directement `Iterable<...>`. Une map possède trois projections naturelles différentes : clés, valeurs et entrées ; aucune ne doit être choisie implicitement comme itération « par défaut ».
@@ -414,19 +516,19 @@ remplace uniquement la valeur d'une clé existante. Cette syntaxe n'insère jama
insert(K key, V value) -> Void
faults DuplicateKeyFault
remove(const K key) -> Void
remove(const K key) -> V
faults KeyNotFoundFault
clear() -> uint64
```
`insert` affirme que la clé est nouvelle ; `remove` affirme qu'elle existe. Leurs violations dynamiques sont des `Fault` unchecked et capturables.
`insert` affirme que la clé est nouvelle ; `remove` affirme qu'elle existe et retourne la valeur retirée. Leurs violations dynamiques sont des `Fault` unchecked et capturables.
Le Core ne fournit pas mécaniquement `tryInsert` / `tryRemove` ayant pour seule fonction de dupliquer ces opérations dans `Result` ou `bool`. Une variante conditionnelle ne sera ajoutée que si elle porte une sémantique réellement utile distincte.
Il n'existe pas de `put()` implicite « insert ou replace » tant qu'un besoin concret ne justifie pas cette troisième intention.
### 22.4.11 `MapEntry<K,V>`
### 22.4.12 `MapEntry<K,V>`
`MapEntry<K,V>` représente une paire clé/valeur observée lors d'une projection `entries()`.
@@ -449,7 +551,7 @@ map[key] = value;
La sémantique de copie/référence de `K` et `V` suit les règles normales de leurs types.
### 22.4.12 Ordre : `Ordering`, `Comparable<T>` et `Comparator<T>`
### 22.4.13 Ordre : `Ordering`, `Comparable<T>` et `Comparator<T>`
L'ordre total applicatif utilise :
@@ -495,7 +597,7 @@ Aucun mélange/fallback entre les deux n'est effectué pendant une même opérat
Les implémentations de `Comparable` et `Comparator` doivent respecter cohérence, symétrie d'ordre et transitivité. Le compilateur n'est pas tenu de prouver ces propriétés générales.
### 22.4.13 `SortedSet<T>` et `SortedMap<K,V>`
### 22.4.14 `SortedSet<T>` et `SortedMap<K,V>`
`SortedSet<T>` étend `Set<T>` et garantit un ordre total stable selon le comparator actif :
@@ -523,7 +625,7 @@ Pour une `SortedMap`, les vues `keys()`, `values()` et `entries()` parcourent le
Les opérations de bornes/ranges (`floor`, `ceiling`, `lower`, `higher` ou noms définitifs) seront fermées séparément afin de ne pas introduire de noms ambigus par simple imitation d'une autre plateforme.
### 22.4.14 `TreeSet<T>` et `TreeMap<K,V>`
### 22.4.15 `TreeSet<T>` et `TreeMap<K,V>`
Les types standard ordonnés généraux sont nommés :
@@ -554,23 +656,162 @@ TreeMap<K,V>::withComparator(Comparator<K> comparator)
Les contraintes appartiennent aux factories concernées et non nécessairement au type générique entier.
### 22.4.15 Contraintes des implémentations concrètes
### 22.4.16 `Equatable<T>`, `Hashable` et `Hasher`
Les interfaces abstraites `Set<T>` et `Map<K,V>` n'imposent pas une stratégie de stockage particulière.
Ainsi :
`Equatable<T>` est un contrat Core de haut niveau fondé sur l'égalité totale `OpEqual<T>` ; il n'ajoute aucune seconde méthode d'égalité :
```text
HashSet<T> / HashMap<K,V>
peuvent exiger des capacités de hash/égalité
TreeSet<T> / TreeMap<K,V>
utilisent Comparable ou Comparator
interface Equatable<T> extends OpEqual<T> {
}
```
`Hashable`, l'égalité et leurs contrats exacts seront fermés dans le bloc dédié. Ils ne sont pas imposés à `Set<T>` ou `Map<K,V>` eux-mêmes.
`Hashable` est un contrat Core indépendant de `Comparable<T>` et de `Equatable<T>` au niveau de l'héritage :
### 22.4.16 Collections concurrentes
```text
interface Hashable {
const method hash(Hasher hasher) -> Void;
}
```
Obligation normative de `Hashable` : **toute donnée participant à l'identité hashable d'une valeur doit rester stable pendant toute la durée de vie de cette valeur**.
Un objet peut donc rester mutable dans ses autres propriétés. Si `id` seul définit son hash et son égalité, des champs tels que `name` ou `score` peuvent changer sans violer `Hashable`.
Lorsqu'un type est à la fois `Equatable<T>` et `Hashable`, le contrat exige :
```text
a == b
-> même résultat de hash avec le même Hasher et la même configuration
```
L'inverse n'est pas requis : une collision de hash entre valeurs non égales est normale.
`Hasher` appartient au Core parce que son contrat doit être connu des valeurs `Hashable` :
```text
interface Hasher {
method writeBytes(const Slice<uint8> bytes) -> Void;
const method finish() -> uint64;
}
```
Les implémentations concrètes de `Hasher` appartiennent au SDK/runtime et peuvent employer des algorithmes/seeds différents.
`finish()` observe l'état courant sans le modifier : deux appels successifs sans `writeBytes()` intermédiaire retournent le même résultat.
Le Core définit le framing canonique utilisé par ses types fondamentaux `Hashable`. Les composants logiques successifs ne doivent pas devenir indistinguables par simple concaténation ambiguë d'octets ; par exemple les couples `("ab","c")` et `("a","bc")` ne peuvent pas être traités comme une unique séquence logique identique.
La représentation injectée dans un `Hasher` est un protocole de hashing interne, pas un format de sérialisation, une ABI ou un format de stockage public.
Les `float*` bruts conservent leur égalité IEEE partielle et ne satisfont pas directement `Equatable<float*>`; ils ne sont donc pas directement admissibles comme clés des collections hashées standard qui exigent `Equatable`.
### 22.4.17 `HashSet<T>` et `HashMap<K,V>`
Les implémentations standard hashées appartiennent au SDK :
```text
HashSet<T>::empty() -> HashSet<T>
HashSet<T>::builder() -> HashSetBuilder<T>
HashMap<K,V>::empty() -> HashMap<K,V>
HashMap<K,V>::builder() -> HashMapBuilder<K,V>
HashSet<T>
implements ResizableSet<T>
where T implements Equatable<T>
where T implements Hashable
HashMap<K,V>
implements ResizableMap<K,V>
where K implements Equatable<K>
where K implements Hashable
```
Elles ne garantissent aucun ordre logique ou d'itération stable. Deux instances contenant les mêmes éléments, ou deux exécutions du même programme, peuvent parcourir les données dans des ordres différents.
Le hasher concret et sa seed ne font pas partie du contrat observable général. Deux instances peuvent employer des seeds distinctes.
Une collision est normale :
```text
même hash + égalité vraie
-> même clé / même élément logique
même hash + égalité fausse
-> collision, les deux valeurs peuvent coexister
```
Il n'existe pas de `HashCollisionFault`.
Garanties de complexité moyennes :
```text
contains / containsKey / get O(1) moyen
add / insert / remove O(1) moyen
count / isEmpty O(1)
```
Le pire cas peut être O(n). Le contrat ne fige ni chaining, ni open addressing, ni Robin Hood, ni autre stratégie physique.
Pour `HashMap`, toutes les règles strictes de `Map`/`ResizableMap` restent valables : `map[key]` exige la clé, `get()` représente l'absence normale, `insert()` exige une clé nouvelle, `remove()` exige une clé existante et retourne `V`.
Pour `HashSet`, `add()` et `remove()` conservent leurs retours booléens ordinaires.
Une réorganisation interne des buckets/rehash est physiquement invisible. Une `View<T>` existante sur `keys()`, `values()` ou `entries()` reste une vue valide de la collection ; un `Iterator<T>` déjà créé peut néanmoins être invalidé par la mutation structurelle selon la règle générale des iterators.
### 22.4.18 Builders des collections concrètes
Les collections dynamiques concrètes utilisent des class methods explicites plutôt que de détourner le littéral `[...]` :
```text
Vector<T>::builder() -> VectorBuilder<T>
HashSet<T>::builder() -> HashSetBuilder<T>
HashMap<K,V>::builder() -> HashMapBuilder<K,V>
```
Contrats minimaux :
```text
class VectorBuilder<T> {
method append(T value) -> VectorBuilder<T>;
const method build() -> Vector<T>;
}
class HashSetBuilder<T> {
method add(T value) -> HashSetBuilder<T>;
const method build() -> HashSet<T>;
}
class HashMapBuilder<K,V> {
method insert(K key, V value) -> HashMapBuilder<K,V>
faults DuplicateKeyFault;
const method build() -> HashMap<K,V>;
}
```
Les builders sont des objets mutables ordinaires. Les méthodes de configuration peuvent retourner le même builder afin de permettre le chaînage :
```text
Vector<int32> values =
Vector<int32>::builder()
::append(1)
::append(2)
::append(3)
::build();
```
`build()` ne consomme pas le builder. Un builder peut produire plusieurs collections successives ; chaque résultat est sémantiquement indépendant des modifications ultérieures du builder. Un partage physique interne/COW reste possible tant que cette indépendance observable est respectée.
Un builder vide produit la collection vide correspondante.
`HashSetBuilder<T>` suit la sémantique du set : ajouter deux valeurs égales ne produit pas un fault et la valeur n'apparaît qu'une fois.
`HashMapBuilder<K,V>` suit la sémantique stricte de `insert` : une seconde clé égale produit `DuplicateKeyFault`; aucune règle implicite « first wins » ou « last wins » n'existe.
Les builders standard ne permettent pas de choisir une seed ou une implémentation de hasher. Ils ne fournissent pas non plus un `reserveExact` caché.
### 22.4.19 Collections concurrentes
Les garanties concurrentes sont orthogonales aux capacités structurelles et relèvent du SDK.