v0.2.18
This commit is contained in:
@@ -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.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user