v0.2.17
This commit is contained in:
@@ -22,6 +22,8 @@ Principe directeur :
|
||||
|
||||
> **boring but working** : une règle stable et prévisible vaut mieux qu'une syntaxe plus courte mais ambiguë.
|
||||
|
||||
Lorsqu'une forme légèrement plus longue supprime un implicite ou une ambiguïté réelle, Saselang privilégie l'explicite. La concision n'est pas un objectif supérieur à la lisibilité du contrat.
|
||||
|
||||
## 2.2 Mots-clés — V1 REQUIS — FIGÉ
|
||||
|
||||
Les mots-clés du langage sont en anglais.
|
||||
|
||||
@@ -17,7 +17,7 @@ unions
|
||||
tuples
|
||||
generics
|
||||
contrôle de flux
|
||||
erreurs/exceptions
|
||||
erreurs/exceptions/faults
|
||||
opérateurs
|
||||
unsafe
|
||||
scope
|
||||
@@ -42,6 +42,7 @@ Result<T,E>
|
||||
Error
|
||||
ResultError
|
||||
Exception
|
||||
Fault
|
||||
Option<T>
|
||||
Nullable<T>
|
||||
Range<T>
|
||||
@@ -50,10 +51,18 @@ RangeStep<T,Step>
|
||||
RangeReverse<T>
|
||||
RangeReverseStep<T,Step>
|
||||
RangeProgression<T,Step>
|
||||
PartialOrdering
|
||||
Ordering
|
||||
Comparable<T>
|
||||
Comparator<T>
|
||||
Op...
|
||||
Iterable<T>
|
||||
Iterator<T>
|
||||
View<T>
|
||||
Collection<T>
|
||||
List<T>
|
||||
Set<T>
|
||||
Map<K,V>
|
||||
Array<T>
|
||||
StaticArray<T,N>
|
||||
TypeInfo
|
||||
@@ -71,7 +80,7 @@ Une capacité Core peut être conditionnée par un target lorsqu'elle ne peut ra
|
||||
Le SDK fournit les fonctionnalités de bibliothèque qui ne justifient pas une sémantique spéciale du compilateur :
|
||||
|
||||
```text
|
||||
collections
|
||||
collections concrètes/spécialisées au-delà des contrats fondamentaux Core
|
||||
encodages explicites
|
||||
regex
|
||||
filesystem
|
||||
|
||||
@@ -49,6 +49,6 @@ Si tous les éléments permettent l'égalité pertinente, le tuple peut être co
|
||||
|
||||
Si tous les éléments sont comparables, l'ordre du tuple est lexicographique.
|
||||
|
||||
Si un composant comparé retourne `Ordering::Unordered`, le tuple est `Unordered`.
|
||||
Si un composant comparé retourne `PartialOrdering::Unordered`, le résultat de comparaison du tuple est `PartialOrdering::Unordered`.
|
||||
|
||||
---
|
||||
|
||||
@@ -41,10 +41,12 @@ type de retour
|
||||
visibilité
|
||||
fallibilité / Result
|
||||
throws
|
||||
contraintes génériques pertinentes
|
||||
qualification const et contraintes génériques pertinentes
|
||||
modificateurs contractuels pertinents
|
||||
```
|
||||
|
||||
`faults` reste visible dans la déclaration et dans la documentation, mais sa nature optionnelle/non exhaustive signifie qu'il n'impose pas la même compatibilité d'override que `throws`.
|
||||
|
||||
Les futurs pré/post-contrats formels, s'ils existent, devront également participer à cette notion.
|
||||
|
||||
## 13.5 Conflits d'héritage multiple — V1 REQUIS — FIGÉ
|
||||
|
||||
@@ -9,37 +9,42 @@ clsmethod méthode de classe
|
||||
operator implémentation d'un contrat opérateur
|
||||
```
|
||||
|
||||
## 15.2 Retours directs et `Result` — V1 REQUIS — FIGÉ
|
||||
## 15.2 Retours directs, `Result`, `throws` et `faults` — V1 REQUIS — FIGÉ
|
||||
|
||||
L'ancienne règle « toute fonction/méthode retourne `Result` » est supprimée.
|
||||
Le type de retour et le mécanisme d'échec sont des dimensions distinctes.
|
||||
|
||||
Règle V1 :
|
||||
Une callable peut retourner directement `T` même si elle peut produire une `Exception` checked ou un `Fault` unchecked :
|
||||
|
||||
```text
|
||||
opération infaillible
|
||||
-> retourne directement T
|
||||
func readConfig(String path) -> Config
|
||||
throws IOException
|
||||
|
||||
opération pouvant produire une erreur récupérable
|
||||
-> retourne Result<T>
|
||||
ou Result<T,E>
|
||||
method elementAt(uint64 index) -> T
|
||||
faults IndexOutOfBoundsFault
|
||||
```
|
||||
|
||||
Exemples infaillibles :
|
||||
`Result<T,E>` est utilisé lorsque l'échec doit être transporté explicitement comme une valeur et inspecté par l'appelant :
|
||||
|
||||
```text
|
||||
method length() -> uint64
|
||||
method containsKey(K key) -> bool
|
||||
Object::sameInstance(Object other) -> bool
|
||||
func min(int32 a, int32 b) -> int32
|
||||
func parseExternalInput(String input) -> Result<Value, ParseError>
|
||||
```
|
||||
|
||||
Exemples faillibles :
|
||||
Les combinaisons sont indépendantes :
|
||||
|
||||
```text
|
||||
func readFile(String path) -> Result<String, IoError>
|
||||
method parse(String input) -> Result<Value, ParseError>
|
||||
T
|
||||
T throws SomeException
|
||||
T faults SomeFault
|
||||
T throws SomeException faults SomeFault
|
||||
Result<T,E>
|
||||
Result<T,E> throws SomeException
|
||||
Result<T,E> faults SomeFault
|
||||
```
|
||||
|
||||
Saselang ne force donc plus une callable à retourner `Result` uniquement parce qu'elle peut échouer. À l'inverse, une API ne doit pas remplacer systématiquement un échec normal du domaine par un `Fault` si `Option` ou `Result` exprime mieux le contrat.
|
||||
|
||||
Le Core ne crée pas mécaniquement des paires `op()` / `tryOp()` ayant pour seule différence « valeur directe avec fault » versus « même opération dans Result ». Deux opérations distinctes doivent porter des sémantiques réellement distinctes.
|
||||
|
||||
## 15.3 Retour explicite — V1 REQUIS — FIGÉ
|
||||
|
||||
Pas de retour implicite de dernière expression.
|
||||
|
||||
@@ -1,25 +1,33 @@
|
||||
# 18. `Result`, erreurs et exceptions
|
||||
# 18. `Result`, erreurs, exceptions et faults
|
||||
|
||||
## 18.1 Hiérarchie Core — V1 REQUIS — FIGÉ
|
||||
|
||||
La hiérarchie d'erreurs V1 est :
|
||||
La hiérarchie V1 est :
|
||||
|
||||
```text
|
||||
Object
|
||||
└── Error
|
||||
├── ResultError
|
||||
└── Exception
|
||||
├── Exception
|
||||
└── Fault
|
||||
```
|
||||
|
||||
Les trois classes racines sont abstraites et ne sont donc jamais instanciées directement.
|
||||
Les quatre classes racines sont abstraites et ne sont jamais instanciées directement.
|
||||
|
||||
`Error` est la racine abstraite commune des erreurs Saselang. Elle ne détermine pas à elle seule le mécanisme de propagation.
|
||||
`Error` est la racine commune. Elle ne choisit pas à elle seule le mécanisme de propagation.
|
||||
|
||||
`ResultError` représente exclusivement la famille des erreurs transportables par `Result<T,E>`.
|
||||
```text
|
||||
ResultError
|
||||
échec transporté explicitement comme une valeur dans Result<T,E>
|
||||
|
||||
`Exception` représente exclusivement la famille des erreurs propagées par `throw` / `throws` / `try` / `catch` / `finally`.
|
||||
Exception
|
||||
échec checked propagé par throw / throws
|
||||
|
||||
`ResultError` et `Exception` sont des branches sœurs. Une classe utilisateur destinée à un `Result` hérite directement ou indirectement de `ResultError`. Une exception utilisateur hérite directement ou indirectement de `Exception`.
|
||||
Fault
|
||||
échec unchecked, capturable, pouvant être documenté par faults
|
||||
```
|
||||
|
||||
Les trois branches sont sœurs. Aucune n'est implicitement convertible vers une autre.
|
||||
|
||||
Exemple :
|
||||
|
||||
@@ -29,9 +37,12 @@ public final class ParseError extends ResultError {
|
||||
|
||||
public final class FileNotFoundException extends Exception {
|
||||
}
|
||||
|
||||
public final class IndexOutOfBoundsFault extends Fault {
|
||||
}
|
||||
```
|
||||
|
||||
L'interface `Throwable` ne fait pas partie de Saselang V1. Elle pourra être réintroduite ultérieurement si un besoin réel apparaît sans modifier la règle fondamentale : `Result` transporte des `ResultError`, tandis que `throw` transporte des `Exception`.
|
||||
Il n'existe pas d'interface `Throwable` en V1.
|
||||
|
||||
## 18.2 Informations communes de `Error` — V1 REQUIS — FIGÉ EN PRINCIPE
|
||||
|
||||
@@ -51,7 +62,7 @@ message
|
||||
|
||||
cause
|
||||
erreur causale purement informative
|
||||
peut contenir un ResultError ou une Exception
|
||||
peut contenir un ResultError, une Exception ou un Fault
|
||||
n'est pas automatiquement propagée
|
||||
|
||||
i18nMessage
|
||||
@@ -59,13 +70,11 @@ i18nMessage
|
||||
ne contient pas un tableau de traductions
|
||||
```
|
||||
|
||||
`cause` est de type `Option<Error>` et non `Nullable<Error>` : l'absence de cause est une absence sémantique. Une cause de type statique `Error` ne peut pas être passée directement à `throw`; il faut d'abord prouver par `is` qu'elle est une `Exception`, ou l'encapsuler explicitement dans une nouvelle exception.
|
||||
`cause` n'accorde aucun droit de propagation. Une valeur statiquement typée `Error` ne peut être utilisée ni avec `throw` ni avec `fault` sans preuve préalable de sa branche exacte.
|
||||
|
||||
`message`, `cause` et `i18nMessage` sont initialisés lors de la construction et ne sont pas publiquement mutables.
|
||||
|
||||
`Error` ne porte pas de propriétés universelles supplémentaires telles que timestamp, thread id, process id, host, severity ou code plateforme. Ces informations appartiennent aux sous-classes, SDKs ou couches de logging/diagnostic lorsqu'elles sont pertinentes.
|
||||
|
||||
Le type exact `I18nMessage`, sa clé et la représentation de ses paramètres seront finalisés avec Core/SDK et le système de formatting/i18n.
|
||||
`Error` ne porte pas de propriétés universelles supplémentaires telles que timestamp, thread id, process id, host, severity ou code plateforme. Ces informations appartiennent aux sous-classes, au SDK ou aux couches de logging/diagnostic lorsqu'elles sont pertinentes.
|
||||
|
||||
## 18.3 `ResultError` et code machine-readable — V1 REQUIS — FIGÉ EN PRINCIPE
|
||||
|
||||
@@ -78,26 +87,31 @@ code: ResultErrorCode
|
||||
Le rôle de `ResultErrorCode` est distinct de `message` :
|
||||
|
||||
```text
|
||||
code -> stable, machine-readable, non localisé
|
||||
message -> humain, canonique / fallback
|
||||
code -> stable, machine-readable, non localisé
|
||||
message -> humain, canonique / fallback
|
||||
i18nMessage -> localisation externe
|
||||
```
|
||||
|
||||
La représentation exacte de `ResultErrorCode` sera finalisée avec Core/SDK. `Exception` n'a pas de code obligatoire.
|
||||
La représentation exacte de `ResultErrorCode` sera finalisée avec Core/SDK.
|
||||
|
||||
## 18.4 `Exception` et stack trace — V1 REQUIS — FIGÉ EN PRINCIPE
|
||||
Ni `Exception` ni `Fault` n'ont de code universel obligatoire ; leurs sous-types peuvent naturellement en définir lorsqu'il est utile.
|
||||
|
||||
`Exception` reste généraliste. Elle peut recevoir des informations de stack trace liées à sa propagation.
|
||||
## 18.4 Diagnostic de `Exception` et `Fault` — V1 REQUIS — FIGÉ EN PRINCIPE
|
||||
|
||||
La création d'un objet `Exception` ne doit pas obligatoirement capturer immédiatement une stack trace. Le contexte de propagation peut être attaché ou capturé lors de :
|
||||
`Exception` et `Fault` peuvent recevoir des informations de stack trace liées à leur propagation runtime.
|
||||
|
||||
La création de l'objet ne doit pas obligatoirement capturer immédiatement une stack trace. Le contexte peut être attaché ou capturé lors de :
|
||||
|
||||
```text
|
||||
throw exception;
|
||||
fault someFault;
|
||||
```
|
||||
|
||||
La représentation exacte (`StackTrace`, `StackFrame`, capture lazy ou autre optimisation) relève de Core/runtime. Cette information reste diagnostique et n'intervient pas dans la sélection des `catch`.
|
||||
ou lorsqu'un `Fault` est produit intrinsèquement par le runtime/Core.
|
||||
|
||||
`Error` et `ResultError` n'ont pas de stack trace automatique obligatoire.
|
||||
La représentation exacte (`StackTrace`, `StackFrame`, capture lazy ou autre optimisation) relève du Core/runtime. Cette information reste diagnostique et n'intervient pas dans la sélection des `catch`.
|
||||
|
||||
`ResultError` n'a pas de stack trace automatique obligatoire.
|
||||
|
||||
## 18.5 `Result<T,E>` — V1 REQUIS — FIGÉ
|
||||
|
||||
@@ -127,6 +141,7 @@ Sont invalides :
|
||||
```text
|
||||
Result<Data,Error>
|
||||
Result<Data,Exception>
|
||||
Result<Data,Fault>
|
||||
Result<Data,FileNotFoundException>
|
||||
```
|
||||
|
||||
@@ -142,7 +157,7 @@ est équivalente à :
|
||||
Result<T,ResultError>
|
||||
```
|
||||
|
||||
`Result<Void,E>` et `Result<Void>` sont autorisés pour représenter un succès sans payload utile. `Option<Void>` reste interdit.
|
||||
`Result<Void,E>` et `Result<Void>` sont autorisés. `Option<Void>` reste interdit.
|
||||
|
||||
Il n'existe aucune conversion implicite de `T` vers `Result<T,E>` ni de `E` vers `Result<T,E>` :
|
||||
|
||||
@@ -151,73 +166,72 @@ return Result::Ok(value);
|
||||
return Result::Err(error);
|
||||
```
|
||||
|
||||
sont explicites.
|
||||
restent explicites.
|
||||
|
||||
`Result` suit les règles générales des enums algébriques. Son inspection et l'extraction sûre de ses payloads utilisent `match` avec bindings typés. V1 n'introduit ni `unwrap`, ni opérateur `?`, ni extraction implicite d'une variante.
|
||||
`Result` suit les règles générales des enums algébriques. V1 n'introduit ni `unwrap`, ni opérateur `?`, ni extraction implicite d'une variante.
|
||||
|
||||
Des helpers Core futurs tels que :
|
||||
## 18.6 Choix du mécanisme d'échec — V1 REQUIS — FIGÉ EN PRINCIPE
|
||||
|
||||
Les trois branches ne sont pas trois orthographes du même concept.
|
||||
|
||||
```text
|
||||
result::expectOk(...)
|
||||
result::expectErr(...)
|
||||
ResultError / Result
|
||||
l'échec est une donnée normale du résultat et l'appelant doit l'inspecter explicitement
|
||||
|
||||
Exception
|
||||
l'échec appartient au contrat checked de la callable et doit être capturé ou propagé par throws
|
||||
|
||||
Fault
|
||||
l'échec est unchecked ; l'appelant peut le capturer mais n'y est pas obligé
|
||||
```
|
||||
|
||||
peuvent être étudiés si un besoin réel apparaît. Ils resteraient une API de `Result`, pas une syntaxe du langage, et leur comportement d'échec devrait être explicitement spécifié.
|
||||
Une API directe n'est donc plus obligée de retourner `Result` uniquement parce qu'elle peut échouer.
|
||||
|
||||
## 18.6 Séparation `ResultError` / `Exception` — V1 REQUIS — FIGÉ
|
||||
|
||||
Aucune conversion automatique n'existe entre les deux branches :
|
||||
Exemple :
|
||||
|
||||
```text
|
||||
ResultError -> Exception
|
||||
Exception -> ResultError
|
||||
method elementAt(uint64 index) -> T
|
||||
faults IndexOutOfBoundsFault
|
||||
```
|
||||
|
||||
La traduction d'un mécanisme vers l'autre se fait par encapsulation explicite dans une nouvelle erreur de la branche cible, éventuellement en conservant l'erreur source dans `cause`.
|
||||
peut retourner directement `T`.
|
||||
|
||||
Exemples conceptuels :
|
||||
Inversement, lorsqu'une absence ou un échec constitue une donnée normale du domaine, `Option` ou `Result` reste préférable :
|
||||
|
||||
```text
|
||||
throw ParseException(..., Option::Some(parseError));
|
||||
map::get(key) -> Option<V>
|
||||
parseExternalInput(...) -> Result<Value, ParseError>
|
||||
```
|
||||
|
||||
ou :
|
||||
Le Core ne doit pas créer mécaniquement un couple `op()` / `tryOp()` uniquement pour offrir d'un côté une valeur directe et de l'autre un `Result`. Deux opérations coexistantes doivent avoir des sémantiques réellement distinctes.
|
||||
|
||||
```text
|
||||
catch (FileNotFoundException error) {
|
||||
return Result::Err(FileResultError(..., Option::Some(error)));
|
||||
}
|
||||
```
|
||||
|
||||
Un simple cast ne transforme pas un `ResultError` en `Exception` ni l'inverse.
|
||||
Aucune conversion automatique n'existe entre `ResultError`, `Exception` et `Fault`. Une traduction entre branches se fait par encapsulation explicite, éventuellement via `cause`.
|
||||
|
||||
## 18.7 `throws` — V1 REQUIS — FIGÉ
|
||||
|
||||
`throws` est interdit sur une `func` / `method` / `clsmethod` à retour direct. Il n'est permis que si le retour est `Result<T>` ou `Result<T,E>`.
|
||||
`throws` est indépendant du type de retour.
|
||||
|
||||
Sont valides :
|
||||
|
||||
```text
|
||||
T + throws -> interdit
|
||||
Result<T,E> -> valide sans throws
|
||||
Result<T,E> + throws -> valide
|
||||
T
|
||||
T throws SomeException
|
||||
Result<T,E>
|
||||
Result<T,E> throws SomeException
|
||||
Void throws SomeException
|
||||
```
|
||||
|
||||
Tout type déclaré dans `throws` doit être `Exception` ou un descendant de `Exception`.
|
||||
|
||||
Une callable doit déclarer toute famille d'exception susceptible d'atteindre sa frontière sans être gérée localement. Cette obligation est transitive : une exception déclarée par une callable appelée doit être soit capturée localement, soit couverte par le `throws` de l'appelant.
|
||||
|
||||
Un type déclaré dans `throws` couvre ses descendants. Une clause :
|
||||
Un type déclaré dans `throws` couvre ses descendants. Une même clause ne doit pas contenir de types redondants lorsqu'un type déclaré couvre déjà entièrement un autre type de la liste.
|
||||
|
||||
```text
|
||||
throws IOException
|
||||
```
|
||||
|
||||
peut donc couvrir `FileNotFoundException` si cette dernière hérite de `IOException`.
|
||||
|
||||
Une même clause `throws` ne doit pas contenir de types redondants lorsqu'un type déclaré couvre déjà entièrement un autre type de la liste.
|
||||
`Fault`, `Error` et `ResultError` sont interdits dans `throws`.
|
||||
|
||||
## 18.8 `throws` dans les contrats, interfaces et overrides — V1 REQUIS — FIGÉ
|
||||
|
||||
`throws` ne fait pas partie de la signature d'overload. Il fait partie du contrat de la callable.
|
||||
`throws` ne fait pas partie de la signature d'overload. Il fait partie du contrat checked de la callable.
|
||||
|
||||
Un override ou une implémentation peut :
|
||||
|
||||
@@ -229,21 +243,20 @@ gérer localement tout ou partie des exceptions du contrat parent
|
||||
|
||||
Il ne peut jamais ajouter une exception non couverte par le contrat parent ni élargir une famille déclarée.
|
||||
|
||||
La même règle s'applique aux contrats d'interface. Lorsque plusieurs interfaces portent la même signature, l'implémentation doit satisfaire simultanément tous leurs contrats `throws`. Une implémentation sans exception sortante est toujours compatible avec un contrat qui en autorise.
|
||||
La même règle s'applique aux contrats d'interface. Une implémentation sans exception sortante est toujours compatible avec un contrat qui en autorise.
|
||||
|
||||
## 18.9 `throw` — V1 REQUIS — FIGÉ
|
||||
|
||||
`throw` ne peut lancer qu'une valeur dont le type statique est `Exception` ou un descendant de `Exception`.
|
||||
|
||||
```text
|
||||
throw FileNotFoundException(...); // valide
|
||||
throw ParseError(...); // erreur
|
||||
throw Error(...); // erreur
|
||||
throw FileNotFoundException(...); // OK
|
||||
throw ParseError(...); // ERROR
|
||||
throw IndexOutOfBoundsFault(...); // ERROR
|
||||
throw Error(...); // ERROR
|
||||
```
|
||||
|
||||
Les racines abstraites `Error`, `ResultError` et `Exception` ne sont jamais instanciables directement.
|
||||
|
||||
La forme de repropagation reste explicite :
|
||||
La repropagation reste explicite :
|
||||
|
||||
```text
|
||||
catch (IOException error) {
|
||||
@@ -253,7 +266,69 @@ catch (IOException error) {
|
||||
|
||||
Il n'existe pas de forme spéciale `throw;` en V1.
|
||||
|
||||
## 18.10 `try` / `catch` / `finally` — V1 REQUIS — FIGÉ
|
||||
## 18.10 `fault` et `faults` — V1 REQUIS — FIGÉ EN PRINCIPE
|
||||
|
||||
`fault` produit explicitement un `Fault` :
|
||||
|
||||
```text
|
||||
fault InvalidStateFault(...);
|
||||
```
|
||||
|
||||
Le type statique de la valeur doit être `Fault` ou un descendant de `Fault`.
|
||||
|
||||
```text
|
||||
fault InvalidStateFault(...); // OK
|
||||
fault FileNotFoundException(...); // ERROR
|
||||
fault ParseError(...); // ERROR
|
||||
```
|
||||
|
||||
Une callable peut documenter des faults significatifs directement dans sa signature :
|
||||
|
||||
```text
|
||||
method elementAt(uint64 index) -> T
|
||||
faults IndexOutOfBoundsFault
|
||||
```
|
||||
|
||||
Plusieurs familles peuvent être listées explicitement :
|
||||
|
||||
```text
|
||||
faults FirstFault, SecondFault
|
||||
```
|
||||
|
||||
Chaque type listé doit être `Fault` ou un descendant de `Fault`; une liste ne doit pas contenir de famille redondante déjà couverte par un parent déclaré.
|
||||
|
||||
La clause `faults` est **optionnelle et non exhaustive**.
|
||||
|
||||
Elle signifie que les faults listés font explicitement partie du contrat/documentation utile de l'API. Elle ne signifie pas qu'aucun autre fault runtime ne peut survenir.
|
||||
|
||||
Contrairement à `throws` :
|
||||
|
||||
```text
|
||||
aucun catch n'est obligatoire
|
||||
aucune propagation de la clause faults n'est obligatoire
|
||||
un appelant n'a pas à recopier les faults d'une callable appelée
|
||||
```
|
||||
|
||||
Exemple valide :
|
||||
|
||||
```text
|
||||
func outer() -> Void {
|
||||
inner(); // inner peut déclarer faults SomeFault
|
||||
return Void;
|
||||
}
|
||||
```
|
||||
|
||||
`outer` peut, mais n'est pas obligé de déclarer à son tour :
|
||||
|
||||
```text
|
||||
faults SomeFault
|
||||
```
|
||||
|
||||
`faults` ne fait pas partie de la signature d'overload et n'impose pas les restrictions de covariance contractuelle de `throws`. Un override peut documenter un ensemble différent de faults, puisque l'absence d'un fault dans la clause n'est jamais une garantie statique d'impossibilité.
|
||||
|
||||
Une API publique qui produit explicitement un fault important devrait normalement le documenter avec `faults`; un outil/linter peut signaler les omissions sans en faire une erreur de compilation du langage.
|
||||
|
||||
## 18.11 `try` / `catch` / `finally` — V1 REQUIS — FIGÉ
|
||||
|
||||
Un `try` doit être suivi d'au moins un `catch`.
|
||||
|
||||
@@ -272,7 +347,7 @@ try {
|
||||
...
|
||||
} catch (SpecificException error) {
|
||||
...
|
||||
} catch (ParentException error) {
|
||||
} catch (SomeFault faultValue) {
|
||||
...
|
||||
} finally {
|
||||
...
|
||||
@@ -281,75 +356,95 @@ try {
|
||||
|
||||
`finally` est facultatif et ne peut apparaître qu'après au moins un `catch`.
|
||||
|
||||
Chaque `catch` contient exactement un type explicite d'`Exception` ou descendant. Il n'existe pas de multi-catch `A | B` en V1 : des blocs `catch` distincts sont utilisés.
|
||||
|
||||
Les `catch` sont évalués dans l'ordre source. Le chevauchement par héritage est normal et utile : les cas spécifiques peuvent précéder un fallback de famille générale.
|
||||
|
||||
Un `catch` entièrement couvert par un `catch` précédent est une erreur de compilation, pas un simple warning.
|
||||
|
||||
Exemple valide :
|
||||
Le type explicite d'un `catch` doit être :
|
||||
|
||||
```text
|
||||
catch (FileNotFoundException error) {
|
||||
...
|
||||
} catch (IOException error) {
|
||||
...
|
||||
}
|
||||
Exception ou un descendant de Exception
|
||||
Fault ou un descendant de Fault
|
||||
```
|
||||
|
||||
Exemple invalide si `FileNotFoundException extends IOException` :
|
||||
Sont donc interdits :
|
||||
|
||||
```text
|
||||
catch (IOException error) {
|
||||
...
|
||||
} catch (FileNotFoundException error) {
|
||||
...
|
||||
}
|
||||
catch (Error error)
|
||||
catch (ResultError error)
|
||||
```
|
||||
|
||||
Un ensemble de `catch` n'a pas à être exhaustif. Toute `Exception` non capturée continue à se propager et doit être couverte par le contrat `throws` de la callable englobante.
|
||||
Il n'existe pas de multi-catch `A | B` en V1.
|
||||
|
||||
Les variables de `catch` suivent les règles normales de scope et de non-shadowing. Des `catch` distincts peuvent réutiliser le même nom parce que leurs scopes sont disjoints.
|
||||
Les `catch` sont évalués dans l'ordre source. Un `catch` entièrement couvert par un précédent dans la même branche d'héritage est une erreur de compilation.
|
||||
|
||||
## 18.11 `finally` non-escaping — V1 REQUIS — FIGÉ
|
||||
Une `Exception` non capturée continue à se propager et doit être couverte par `throws`.
|
||||
|
||||
`finally` exécute un bloc commun avant de quitter la construction `try/catch`, qu'il y ait :
|
||||
Un `Fault` non capturé continue à se propager de façon unchecked ; aucune clause `faults` n'est exigée sur les callables traversées.
|
||||
|
||||
Les variables de `catch` suivent les règles normales de scope et de non-shadowing.
|
||||
|
||||
## 18.12 `finally` non-escaping — V1 REQUIS — FIGÉ EN PRINCIPE
|
||||
|
||||
`finally` exécute un bloc commun avant de quitter la construction `try/catch`, notamment sur :
|
||||
|
||||
```text
|
||||
fin normale du try
|
||||
fin normale d'un catch
|
||||
return traversant la construction
|
||||
throw propagé
|
||||
Fault propagé
|
||||
break / continue traversant la construction
|
||||
Exception non capturée par les catch
|
||||
```
|
||||
|
||||
`finally` ne doit jamais remplacer une sortie déjà en cours. Les sorties structurées suivantes y sont interdites :
|
||||
`finally` ne doit pas remplacer volontairement une sortie déjà en cours. Les sorties structurées explicites suivantes y sont interdites :
|
||||
|
||||
```text
|
||||
return
|
||||
throw
|
||||
fault
|
||||
break
|
||||
continue
|
||||
emit
|
||||
```
|
||||
|
||||
Aucune `Exception` non capturée ne peut sortir indirectement d'un `finally`. Toute callable appelée depuis `finally` dont le contrat comporte `throws` doit avoir ses exceptions entièrement gérées à l'intérieur du `finally`.
|
||||
Toute `Exception` produite indirectement depuis `finally` doit être gérée localement selon les règles checked de `throws`.
|
||||
|
||||
Une faute runtime non récupérable reste distincte de ce contrat d'exception.
|
||||
Un `Fault` dynamique peut néanmoins survenir dans le code exécuté par `finally`, puisqu'il est unchecked. L'interaction exacte entre un tel fault, l'unwind, les destructeurs et une sortie déjà en cours reste à fermer avec le modèle mémoire/runtime.
|
||||
|
||||
## 18.12 Faute runtime / panic — V1 REQUIS — À FINALISER
|
||||
## 18.13 Sémantique runtime des `Fault` — V1 REQUIS — FIGÉ EN PRINCIPE
|
||||
|
||||
Les violations de contrat du langage telles que :
|
||||
Les `Fault` couvrent notamment les échecs unchecked et violations runtime tels que :
|
||||
|
||||
```text
|
||||
index hors limites
|
||||
division entière par zéro
|
||||
index dynamique hors limites
|
||||
division entière dynamique par zéro
|
||||
overflow checked
|
||||
représentation mémoire invalide
|
||||
borne dynamique invalide lors d'une construction directe lorsque la règle du type le définit
|
||||
iterator invalidé
|
||||
clé absente lors d'un accès strict map[key]
|
||||
doublon lors d'un insert strict
|
||||
conversion explicite directe dont les préconditions runtime ne sont pas satisfaites
|
||||
représentation/encodage invalide lors d'une construction directe validée
|
||||
```
|
||||
|
||||
ne sont pas nécessairement des `ResultError` ou des `Exception` récupérables.
|
||||
Lorsqu'une violation d'une précondition intrinsèque est démontrable statiquement, le compilateur doit la diagnostiquer plutôt que générer un programme condamné à fauter.
|
||||
|
||||
Le modèle exact de faute runtime/fatal error/panic et son interaction avec cleanup/destruction doit être défini avant la finalisation V1.
|
||||
Exemple :
|
||||
|
||||
```text
|
||||
StaticArray<int32,3> values = [1, 2, 3];
|
||||
int32 value = values[5]; // ERROR compilation
|
||||
```
|
||||
|
||||
Cette règle ne rend évidemment pas illégal un statement `fault` explicite : `fault SomeFault(...)` est un mécanisme volontaire du programme et peut être utilisé pour implémenter une API.
|
||||
|
||||
Lorsqu'un `Fault` ne peut être déterminé qu'au runtime :
|
||||
|
||||
```text
|
||||
capturé par catch
|
||||
-> le catch correspondant s'exécute
|
||||
|
||||
non capturé
|
||||
-> le fault remonte sans contrat checked
|
||||
-> à la frontière de l'unité d'exécution, il provoque un échec runtime diagnostiqué
|
||||
```
|
||||
|
||||
La définition exacte de « l'unité d'exécution » (processus, thread, task, isolate éventuel) et l'ordre final cleanup/destruction lors d'un fault seront fermés avec les chapitres mémoire et concurrence.
|
||||
|
||||
---
|
||||
|
||||
@@ -27,7 +27,7 @@ Un `if` qui contient `emit` devient un `if` producteur de valeur. Dans ce cas :
|
||||
- le résultat du `if` doit obligatoirement être affecté à une destination ;
|
||||
- un bloc `else` final est obligatoire ;
|
||||
- chaque voie de terminaison normale de chaque branche doit produire explicitement une valeur via `emit` ;
|
||||
- une voie que le compilateur prouve comme terminant définitivement le contrôle (`return`, `throw`, runtime fault certain ou non-terminaison prouvée) n'a pas à exécuter `emit` ;
|
||||
- une voie que le compilateur prouve comme terminant définitivement le contrôle (`return`, `throw`, `fault` explicite ou non-terminaison prouvée) n'a pas à exécuter `emit` ;
|
||||
- les valeurs émises doivent être compatibles avec le type de la destination ;
|
||||
- aucune valeur de secours n'est implicite, y compris pour `Option<T>`.
|
||||
|
||||
|
||||
@@ -6,13 +6,15 @@ Les opérateurs utilisateur ne peuvent être fournis que via un ensemble fermé
|
||||
|
||||
L'utilisateur ne peut pas inventer de nouveaux symboles opérateurs.
|
||||
|
||||
Les `operator` contracts :
|
||||
Les contrats `operator` :
|
||||
|
||||
- retournent directement leur valeur ;
|
||||
- ne retournent jamais `Result` ;
|
||||
- ne déclarent jamais `throws`.
|
||||
- ne déclarent jamais `throws` ;
|
||||
- peuvent produire des `Fault` unchecked lorsque le contrat de l'opération le prévoit ;
|
||||
- peuvent documenter ces faults par une clause `faults`.
|
||||
|
||||
Une faute de contrat du langage peut néanmoins déclencher une faute runtime déterministe.
|
||||
Une opération dont la précondition est statiquement prouvée fausse est rejetée à la compilation lorsque la règle du langage permet cette preuve. Une violation seulement dynamique produit le `Fault` correspondant.
|
||||
|
||||
## 20.2 Interfaces arithmétiques — V1 REQUIS — FIGÉ
|
||||
|
||||
@@ -124,29 +126,38 @@ Les classes ne reçoivent aucune comparaison champ-à-champ automatique : elles
|
||||
|
||||
## 20.6 Comparaison — V1 REQUIS — FIGÉ EN PRINCIPE
|
||||
|
||||
Core :
|
||||
Core distingue l'ordre partiel de l'ordre total :
|
||||
|
||||
```text
|
||||
public enum Ordering {
|
||||
public enum PartialOrdering {
|
||||
Less,
|
||||
Equal,
|
||||
Greater,
|
||||
Unordered
|
||||
}
|
||||
|
||||
public enum Ordering {
|
||||
Less,
|
||||
Equal,
|
||||
Greater
|
||||
}
|
||||
```
|
||||
|
||||
Interfaces :
|
||||
Interfaces opérateur :
|
||||
|
||||
```text
|
||||
OpPartialCompare<Lhs,Rhs>
|
||||
-> PartialOrdering
|
||||
|
||||
OpCompare<T>
|
||||
-> Ordering
|
||||
```
|
||||
|
||||
`OpCompare<T>` renforce `OpPartialCompare<T,T>` et garantit un ordre total, donc pas de `Ordering::Unordered`.
|
||||
`OpCompare<T>` garantit un ordre total. Les floats utilisent l'ordre partiel pour leurs opérateurs généraux en raison de `NaN`; les entiers peuvent utiliser l'ordre total.
|
||||
|
||||
Les floats utilisent l'ordre partiel ; les entiers peuvent utiliser l'ordre total.
|
||||
`Ordering::Equal` signifie équivalence dans la relation d'ordre considérée. Il ne doit pas être confondu automatiquement avec l'égalité générale `==`.
|
||||
|
||||
Le lien exact entre égalité et comparaison pour éviter deux contrats contradictoires sur une même paire doit être verrouillé dans la spécification finale des interfaces `Op`.
|
||||
Les contrats applicatifs `Comparable<T>` et `Comparator<T>` des collections ordonnées utilisent `Ordering` et sont définis au chapitre 22.
|
||||
|
||||
## 20.7 Comparaisons hétérogènes — V1 REQUIS — FIGÉ EN PRINCIPE
|
||||
|
||||
@@ -187,21 +198,25 @@ D'autres index numériques pourront être supportés ultérieurement sans modifi
|
||||
|
||||
Un type utilisateur peut utiliser n'importe quel type d'index pertinent.
|
||||
|
||||
## 20.10 Map — V1 REQUIS — DIRECTION FIGÉE
|
||||
## 20.10 Map — V1 REQUIS — FIGÉ EN PRINCIPE
|
||||
|
||||
Direction recommandée :
|
||||
Le contrat des maps est défini au chapitre 22.
|
||||
|
||||
L'indexation est stricte :
|
||||
|
||||
```text
|
||||
Map<K,V>
|
||||
OpIndex<K,Option<V>>
|
||||
OpIndexMut<K,V>
|
||||
map[key] -> V
|
||||
```
|
||||
|
||||
Lecture : absent -> `None` ; présent -> `Some(value)`.
|
||||
La clé doit exister ; une absence dynamique produit `KeyNotFoundFault`.
|
||||
|
||||
Écriture : insertion si absent, remplacement si présent.
|
||||
L'accès conditionnel est explicite et distinct :
|
||||
|
||||
Une méthode `containsKey` peut compléter l'API sans être obligatoire avant toute lecture.
|
||||
```text
|
||||
map::get(key) -> Option<V>
|
||||
```
|
||||
|
||||
`OpIndexMut<K,V>` remplace uniquement la valeur d'une clé existante ; l'affectation indexée n'insère jamais implicitement une nouvelle clé.
|
||||
|
||||
## 20.11 Affectation — V1 REQUIS — FIGÉ
|
||||
|
||||
|
||||
@@ -95,26 +95,37 @@ Aucun `Utf8CodeUnit` / `Utf16CodeUnit` / `Utf32CodeUnit` distinct n'est introdui
|
||||
|
||||
Aucun transcodage implicite n'existe.
|
||||
|
||||
Les conversions totales utilisent `to...`.
|
||||
|
||||
Les constructions ou réductions pouvant échouer utilisent `tryFrom...` / `tryTo...`.
|
||||
Les conversions directes retournent la destination attendue. Lorsqu'une validation runtime peut échouer, la callable peut déclarer `faults UnicodeEncodingFault` au lieu de forcer un `ResultError` ou une variante `try...` parallèle.
|
||||
|
||||
Exemples :
|
||||
|
||||
```text
|
||||
char::toUtf8Char()
|
||||
Utf8Char::toChar()
|
||||
Utf8Char::toUtf16Char()
|
||||
char::toUtf8Char() -> Utf8Char
|
||||
Utf8Char::toChar() -> char
|
||||
Utf8Char::toUtf16Char() -> Utf16Char
|
||||
|
||||
Utf8Char::tryFrom(uint8)
|
||||
Utf16Char::tryFrom(uint16)
|
||||
Utf32Char::tryFrom(uint32)
|
||||
Utf8Char::from(uint8) -> Utf8Char
|
||||
faults UnicodeEncodingFault
|
||||
|
||||
Utf8Char::tryToUint8()
|
||||
Utf16Char::tryToUint16()
|
||||
Utf32Char::toUint32()
|
||||
Utf16Char::from(uint16) -> Utf16Char
|
||||
faults UnicodeEncodingFault
|
||||
|
||||
Utf32Char::from(uint32) -> Utf32Char
|
||||
faults UnicodeEncodingFault
|
||||
|
||||
Utf8Char::toUint8() -> uint8
|
||||
faults UnicodeEncodingFault
|
||||
|
||||
Utf16Char::toUint16() -> uint16
|
||||
faults UnicodeEncodingFault
|
||||
|
||||
Utf32Char::toUint32() -> uint32
|
||||
```
|
||||
|
||||
`UnicodeEncodingFault extends Fault` est le nom de travail du fault Core associé aux données encodées invalides ou aux réductions impossibles.
|
||||
|
||||
Le Core n'expose pas simultanément `from(...)` et `tryFrom(...)` lorsque les deux opérations auraient exactement la même sémantique et ne différeraient que par le canal d'échec.
|
||||
|
||||
La matrice normative détaillée est définie dans `annexes/B-unicode-encoding-conversions.md`.
|
||||
|
||||
## 21.5 Aucun mélange implicite de types — V1 REQUIS — FIGÉ
|
||||
|
||||
@@ -135,9 +135,9 @@ Les indices d'une sous-slice sont relatifs à la slice source.
|
||||
|
||||
Les interfaces décrivent des capacités réellement garanties. Les classes/structs génériques fournissent le stockage et l'implémentation.
|
||||
|
||||
Saselang ne retient pas le modèle d'opérations optionnelles qui existent dans une interface mais échouent ensuite au runtime comme « unsupported ».
|
||||
Saselang ne retient pas le modèle d'opérations optionnelles présentes dans une interface mais susceptibles d'échouer ensuite comme « unsupported ».
|
||||
|
||||
Hiérarchie principale retenue :
|
||||
Hiérarchie principale :
|
||||
|
||||
```text
|
||||
Iterable<T>
|
||||
@@ -151,21 +151,84 @@ SettableList<T>
|
||||
ResizableList<T>
|
||||
```
|
||||
|
||||
`Set<T>` et `Map<K,V>` sont des branches distinctes à préciser.
|
||||
`Set<T>` et `Map<K,V>` forment des branches sémantiques distinctes.
|
||||
|
||||
### 22.4.1 `Iterable<T>` / `Iterator<T>`
|
||||
### 22.4.1 `Iterable<T>` et `Iterator<T>`
|
||||
|
||||
Interfaces Core reconnues par `foreach`.
|
||||
`foreach` dépend uniquement de `Iterable<T>`.
|
||||
|
||||
Elles ne portent pas le préfixe `Op`, car `foreach` est une construction du langage et non un opérateur symbolique.
|
||||
```text
|
||||
interface Iterable<T> {
|
||||
const method iterator() -> Iterator<T>;
|
||||
}
|
||||
```
|
||||
|
||||
Indexabilité et itérabilité sont indépendantes.
|
||||
Créer un iterator ne constitue pas une mutation sémantique de la source.
|
||||
|
||||
### 22.4.2 `Collection<T>`
|
||||
`Iterator<T>` représente une itération particulière et possède un état de progression mutable :
|
||||
|
||||
```text
|
||||
interface Iterator<T> {
|
||||
method next() -> Option<T>;
|
||||
}
|
||||
```
|
||||
|
||||
`Some(value)` fournit l'élément suivant ; `None` marque la fin normale de l'itération.
|
||||
|
||||
Saselang n'impose pas le couple `hasNext()` / `next()`. `Option<T>` rend l'état de fin explicite dans une seule opération.
|
||||
|
||||
`Iterator<T>` n'étend pas `Iterable<T>` par défaut. Une source capable de créer des itérations et une itération déjà en cours sont deux capacités différentes, même si certains types concrets peuvent éventuellement fournir les deux.
|
||||
|
||||
Pour les collections ordinaires non concurrentes, une mutation structurelle invalide par défaut les iterators actifs lorsque le type concret ne garantit pas explicitement une autre politique.
|
||||
|
||||
```text
|
||||
insert / remove / clear / append / changement structurel
|
||||
-> peut invalider l'iterator
|
||||
|
||||
utilisation ultérieure d'un iterator invalidé
|
||||
-> IteratorInvalidatedFault
|
||||
```
|
||||
|
||||
Le simple remplacement d'une valeur existante n'est pas automatiquement une mutation structurelle. Le type concret documente sa politique exacte.
|
||||
|
||||
Les collections concurrentes ou spécialisées peuvent fournir une autre politique (`snapshot`, stabilité, weak consistency, etc.) ; elle doit être explicite.
|
||||
|
||||
### 22.4.2 `View<T>`
|
||||
|
||||
`View<T>` représente une vue légère sur une source existante :
|
||||
|
||||
```text
|
||||
interface View<T> extends Iterable<T> {
|
||||
const method count() -> uint64;
|
||||
const method isEmpty() -> bool;
|
||||
}
|
||||
```
|
||||
|
||||
`View<T>` n'étend pas `Collection<T>`.
|
||||
|
||||
Le contrat général ne contient pas `contains()`. Une classe ou une interface plus spécialisée peut l'ajouter si elle en a réellement besoin.
|
||||
|
||||
Une `View<T>` :
|
||||
|
||||
```text
|
||||
n'impose aucune copie des éléments
|
||||
n'impose aucun stockage indépendant
|
||||
n'impose aucune contiguïté
|
||||
ne possède pas nécessairement les données qu'elle expose
|
||||
ne peut jamais augmenter les permissions const de sa source
|
||||
```
|
||||
|
||||
Par défaut, une vue est **vivante**, pas un snapshot : une nouvelle itération peut observer les modifications ultérieures de la source selon le contrat du type concret.
|
||||
|
||||
Le runtime/compilateur garantit que le stockage ou l'état source nécessaire reste valide aussi longtemps que la vue l'exige, sans syntaxe de lifetime utilisateur.
|
||||
|
||||
Le comportement d'un iterator déjà créé avant une modification reste régi par la politique d'invalidation/stabilité du type concret.
|
||||
|
||||
### 22.4.3 `Collection<T>`
|
||||
|
||||
`Collection<T>` étend `Iterable<T>` et représente une collection finie d'éléments.
|
||||
|
||||
Contrat minimal retenu :
|
||||
Contrat minimal :
|
||||
|
||||
```text
|
||||
count() -> uint64
|
||||
@@ -175,7 +238,7 @@ contains(const T value) -> bool
|
||||
|
||||
`count()` exprime le nombre d'éléments au sens général de collection.
|
||||
|
||||
### 22.4.3 `List<T>`
|
||||
### 22.4.4 `List<T>`
|
||||
|
||||
`List<T>` étend `Collection<T>` et `OpIndex<uint64,T>`.
|
||||
|
||||
@@ -193,33 +256,39 @@ Pour une `List<T>` :
|
||||
count() == length()
|
||||
```
|
||||
|
||||
Les deux noms sont néanmoins conservés parce qu'ils expriment des concepts différents : cardinalité générale de collection et longueur d'une séquence indexable.
|
||||
Les deux noms restent distincts parce qu'ils expriment la cardinalité générale d'une collection et la longueur d'une séquence indexable.
|
||||
|
||||
`List<T>` ne garantit ni remplacement d'un élément, ni redimensionnement, ni complexité algorithmique particulière de l'accès indexé.
|
||||
`List<T>` ne garantit ni remplacement, ni redimensionnement, ni complexité algorithmique particulière de l'accès indexé.
|
||||
|
||||
### 22.4.4 `SettableList<T>`
|
||||
### 22.4.5 `SettableList<T>`
|
||||
|
||||
`SettableList<T>` étend `List<T>` et `OpIndexMut<uint64,T>`.
|
||||
|
||||
Elle garantit le remplacement d'un élément existant sans modification de la longueur.
|
||||
Elle garantit le remplacement d'un élément existant sans modification de longueur :
|
||||
|
||||
```text
|
||||
list[index] = value;
|
||||
```
|
||||
|
||||
ne signifie jamais `append` lorsque `index == length()`.
|
||||
`index` doit désigner une case existante ; l'affectation ne signifie jamais `append`.
|
||||
|
||||
### 22.4.5 `ResizableList<T>`
|
||||
### 22.4.6 `ResizableList<T>`
|
||||
|
||||
`ResizableList<T>` étend `SettableList<T>`.
|
||||
`ResizableList<T>` étend `SettableList<T>` et garantit des opérations structurelles capables de modifier la longueur.
|
||||
|
||||
Elle garantit des opérations structurelles capables de modifier la longueur, par exemple ajout, insertion et suppression.
|
||||
Les signatures exactes seront figées avec `Vector<T>`.
|
||||
|
||||
Les signatures exactes seront figées lors de la définition du type concret redimensionnable.
|
||||
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.
|
||||
|
||||
Un `resize(newLength)` sans information d'initialisation n'est pas retenu implicitement : agrandir une collection doit toujours définir comment les nouveaux éléments sont construits.
|
||||
Lorsqu'il existe, `clear()` suit la convention :
|
||||
|
||||
### 22.4.6 Capacité du type et permission `const`
|
||||
```text
|
||||
clear() -> uint64
|
||||
```
|
||||
|
||||
et retourne le nombre d'éléments effectivement supprimés.
|
||||
|
||||
### 22.4.7 Capacité du type et permission `const`
|
||||
|
||||
La capacité intrinsèque du type et la permission d'un accès sont deux dimensions distinctes.
|
||||
|
||||
@@ -230,9 +299,9 @@ const SettableList<User> readonly = users;
|
||||
|
||||
Le type sait remplacer des éléments, mais l'accès `readonly` ne peut pas utiliser cette capacité.
|
||||
|
||||
La propagation générale de `const` reste applicable aux éléments obtenus via cet accès.
|
||||
La même règle s'applique à `Map`, `Set`, `View` et aux éléments obtenus à travers ces accès : une projection ne peut jamais augmenter les droits reçus de sa source.
|
||||
|
||||
### 22.4.7 Classification initiale
|
||||
### 22.4.8 Classification initiale des séquences
|
||||
|
||||
```text
|
||||
StaticArray<T,N>
|
||||
@@ -258,9 +327,271 @@ Vector<T>
|
||||
|
||||
`Vector<T>` reste à définir précisément.
|
||||
|
||||
Un type persistant/immutable par nature n'a aucune obligation d'implémenter `List<T>`. Par exemple, un futur `PersistentList<T>` peut implémenter uniquement `Collection<T>` et fournir ses propres opérations si cela correspond mieux à sa sémantique.
|
||||
Un type persistant/immutable par nature n'a aucune obligation d'implémenter `List<T>` s'il ne garantit pas sa sémantique.
|
||||
|
||||
Une interface Saselang n'est jamais implémentée uniquement parce qu'un type ressemble conceptuellement à une autre famille de types.
|
||||
### 22.4.9 `Set<T>` et `ResizableSet<T>`
|
||||
|
||||
`Set<T>` étend `Collection<T>`.
|
||||
|
||||
Il garantit :
|
||||
|
||||
```text
|
||||
unicité des éléments
|
||||
aucun index ordinal
|
||||
aucun ordre d'itération général garanti
|
||||
```
|
||||
|
||||
La capacité structurelle est séparée :
|
||||
|
||||
```text
|
||||
interface ResizableSet<T> extends Set<T> {
|
||||
method add(T value) -> bool;
|
||||
method remove(const T value) -> bool;
|
||||
method clear() -> uint64;
|
||||
}
|
||||
```
|
||||
|
||||
Sémantique :
|
||||
|
||||
```text
|
||||
add(value)
|
||||
true -> élément ajouté
|
||||
false -> élément déjà présent, set inchangé
|
||||
|
||||
remove(value)
|
||||
true -> élément supprimé
|
||||
false -> élément absent
|
||||
|
||||
clear()
|
||||
-> nombre d'éléments effectivement supprimés
|
||||
```
|
||||
|
||||
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>`
|
||||
|
||||
`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 ».
|
||||
|
||||
Contrat de lecture :
|
||||
|
||||
```text
|
||||
interface Map<K,V> extends OpIndex<K,V> {
|
||||
const method count() -> uint64;
|
||||
const method isEmpty() -> bool;
|
||||
const method containsKey(const K key) -> bool;
|
||||
const method get(const K key) -> Option<V>;
|
||||
|
||||
const method keys() -> View<K>;
|
||||
const method values() -> View<V>;
|
||||
const method entries() -> View<MapEntry<K,V>>;
|
||||
}
|
||||
```
|
||||
|
||||
Deux intentions sont séparées :
|
||||
|
||||
```text
|
||||
map[key] -> V
|
||||
accès strict
|
||||
la clé doit exister
|
||||
absence dynamique -> KeyNotFoundFault
|
||||
|
||||
map::get(key) -> Option<V>
|
||||
accès conditionnel
|
||||
absence normale -> None
|
||||
```
|
||||
|
||||
`SettableMap<K,V>` étend `Map<K,V>` et `OpIndexMut<K,V>`.
|
||||
|
||||
```text
|
||||
map[key] = value;
|
||||
```
|
||||
|
||||
remplace uniquement la valeur d'une clé existante. Cette syntaxe n'insère jamais implicitement une nouvelle clé. Une clé absente produit `KeyNotFoundFault`.
|
||||
|
||||
`ResizableMap<K,V>` étend `SettableMap<K,V>` et ajoute les changements structurels :
|
||||
|
||||
```text
|
||||
insert(K key, V value) -> Void
|
||||
faults DuplicateKeyFault
|
||||
|
||||
remove(const K key) -> Void
|
||||
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.
|
||||
|
||||
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>`
|
||||
|
||||
`MapEntry<K,V>` représente une paire clé/valeur observée lors d'une projection `entries()`.
|
||||
|
||||
Conceptuellement :
|
||||
|
||||
```text
|
||||
struct MapEntry<K,V> {
|
||||
const method key() -> K;
|
||||
const method value() -> V;
|
||||
}
|
||||
```
|
||||
|
||||
`MapEntry` n'est pas un proxy mutable vers un bucket interne de la map et n'expose pas de `setValue()` implicite.
|
||||
|
||||
La mutation d'une map reste explicite via :
|
||||
|
||||
```text
|
||||
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>`
|
||||
|
||||
L'ordre total applicatif utilise :
|
||||
|
||||
```text
|
||||
enum Ordering {
|
||||
Less,
|
||||
Equal,
|
||||
Greater
|
||||
}
|
||||
```
|
||||
|
||||
`Comparable<T>` exprime l'ordre naturel ou principal du type dans son domaine :
|
||||
|
||||
```text
|
||||
interface Comparable<T> {
|
||||
const method compareTo(const T other) -> Ordering;
|
||||
}
|
||||
```
|
||||
|
||||
Un type peut tout à fait implémenter `Comparable<T>` tout en supportant plusieurs ordres alternatifs via des comparators. Par exemple, un `User` d'un jeu peut avoir un classement principal naturel et rester comparable autrement par nom, niveau ou date.
|
||||
|
||||
`Comparator<T>` exprime un ordre externe/alternatif :
|
||||
|
||||
```text
|
||||
interface Comparator<T> {
|
||||
const method compare(const T left, const T right) -> Ordering;
|
||||
}
|
||||
```
|
||||
|
||||
Règle de sélection :
|
||||
|
||||
```text
|
||||
Comparator explicitement fourni
|
||||
-> utilisé exclusivement
|
||||
|
||||
aucun Comparator fourni
|
||||
-> Comparable<T> requis
|
||||
```
|
||||
|
||||
Aucun mélange/fallback entre les deux n'est effectué pendant une même opération ou dans une même collection ordonnée.
|
||||
|
||||
`Ordering::Equal` signifie équivalence selon cette relation d'ordre. Il n'implique pas nécessairement `left == right`.
|
||||
|
||||
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>`
|
||||
|
||||
`SortedSet<T>` étend `Set<T>` et garantit un ordre total stable selon le comparator actif :
|
||||
|
||||
```text
|
||||
interface SortedSet<T> extends Set<T> {
|
||||
const method comparator() -> Option<Comparator<T>>;
|
||||
const method first() -> Option<T>;
|
||||
const method last() -> Option<T>;
|
||||
}
|
||||
```
|
||||
|
||||
`None` pour `comparator()` signifie que l'ordre naturel `Comparable<T>` est utilisé. `Some(comparator)` expose l'ordre externe choisi pour cette instance.
|
||||
|
||||
`SortedMap<K,V>` étend `Map<K,V>` et ordonne les entrées par clé :
|
||||
|
||||
```text
|
||||
interface SortedMap<K,V> extends Map<K,V> {
|
||||
const method comparator() -> Option<Comparator<K>>;
|
||||
const method firstKey() -> Option<K>;
|
||||
const method lastKey() -> Option<K>;
|
||||
}
|
||||
```
|
||||
|
||||
Pour une `SortedMap`, les vues `keys()`, `values()` et `entries()` parcourent les données dans l'ordre des clés.
|
||||
|
||||
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>`
|
||||
|
||||
Les types standard ordonnés généraux sont nommés :
|
||||
|
||||
```text
|
||||
TreeSet<T>
|
||||
TreeMap<K,V>
|
||||
```
|
||||
|
||||
Le nom n'impose pas une structure arborescente précise au niveau ABI/implémentation tant que le contrat observable reste respecté.
|
||||
|
||||
Un futur `BTreeMap<K,V>` peut exister comme implémentation explicitement fondée sur un B-tree ; il ne remplace pas le nom général `TreeMap`.
|
||||
|
||||
Comme Saselang n'autorise qu'un `construct` par classe, les deux modes de création passent par des factories de classe distinctes :
|
||||
|
||||
```text
|
||||
TreeSet<T>::natural()
|
||||
requiert T implements Comparable<T>
|
||||
|
||||
TreeSet<T>::withComparator(Comparator<T> comparator)
|
||||
n'exige pas Comparable<T>
|
||||
|
||||
TreeMap<K,V>::natural()
|
||||
requiert K implements Comparable<K>
|
||||
|
||||
TreeMap<K,V>::withComparator(Comparator<K> comparator)
|
||||
n'exige pas Comparable<K>
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
Les interfaces abstraites `Set<T>` et `Map<K,V>` n'imposent pas une stratégie de stockage particulière.
|
||||
|
||||
Ainsi :
|
||||
|
||||
```text
|
||||
HashSet<T> / HashMap<K,V>
|
||||
peuvent exiger des capacités de hash/égalité
|
||||
|
||||
TreeSet<T> / TreeMap<K,V>
|
||||
utilisent Comparable ou Comparator
|
||||
```
|
||||
|
||||
`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.
|
||||
|
||||
### 22.4.16 Collections concurrentes
|
||||
|
||||
Les garanties concurrentes sont orthogonales aux capacités structurelles et relèvent du SDK.
|
||||
|
||||
Une future :
|
||||
|
||||
```text
|
||||
ConcurrentMap<K,V>
|
||||
```
|
||||
|
||||
exprime des garanties de concurrence/atomicité et ne remplace pas `ResizableMap<K,V>`.
|
||||
|
||||
Un type concret peut par exemple implémenter les deux :
|
||||
|
||||
```text
|
||||
ConcurrentHashMap<K,V>
|
||||
implements ResizableMap<K,V>, ConcurrentMap<K,V>
|
||||
```
|
||||
|
||||
Le SDK n'introduit pas une variante `ConcurrentX` pour chaque type uniquement par symétrie. Les interfaces concurrentes ne sont ajoutées que lorsqu'un contrat atomique/concurrent concret existe.
|
||||
|
||||
Une opération composée n'est pas atomique simplement parce que chaque appel individuel est thread-safe. Les futures opérations atomiques (`putIfAbsent`, comparaison/remplacement, etc.) seront spécifiées avec le modèle de concurrence.
|
||||
|
||||
## 22.5 `Range<T>` — V1 REQUIS — FIGÉ EN PRINCIPE
|
||||
|
||||
@@ -308,7 +639,7 @@ valeur finie autorisée
|
||||
|
||||
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.
|
||||
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 `Fault` runtime lors de la construction directe. Le nom concret du fault sera fixé avec l'API Core ; aucune variante `try... -> Result` parallèle n'est créée automatiquement pour la même validation.
|
||||
|
||||
`lower > upper` ne constitue pas une erreur : le résultat est un range vide.
|
||||
|
||||
@@ -392,7 +723,7 @@ 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.
|
||||
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 `Fault` runtime lors de la construction directe de la progression. Aucune variante `try... -> Result` n'est ajoutée uniquement pour dupliquer ce contrôle.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ Il n'existe aucune promotion numérique implicite générale entre deux valeurs
|
||||
|
||||
```text
|
||||
int8 small = ...;
|
||||
int32 large = small; // ERROR
|
||||
int32 large = small; // ERROR
|
||||
int32 explicitLarge = small::toInt32(); // OK
|
||||
```
|
||||
|
||||
@@ -48,63 +48,140 @@ if (animal is Dog) {
|
||||
}
|
||||
```
|
||||
|
||||
Lorsqu'un test du type runtime **exact** est nécessaire, `instanceof` est utilisé.
|
||||
Lorsqu'un test du type runtime exact est nécessaire, `instanceof` est utilisé.
|
||||
|
||||
Saselang n'introduit pas pour le moment de syntaxe générale concurrente telle que `(Dog)value`, `value as Dog` ou `cast<Dog>(value)`. Une telle opération ne pourra être ajoutée que si un besoin distinct de `is`/`instanceof` + refinement est démontré et si son contrat d'échec est explicite.
|
||||
|
||||
## 24.5 API numérique du Core — V1 REQUIS — DIRECTION FIGÉE
|
||||
## 24.5 API numérique du Core — V1 REQUIS — FIGÉ EN PRINCIPE
|
||||
|
||||
Les conversions numériques sont exposées comme membres standard des primitives définis par le Core. Les noms de méthodes ne sont pas des mots-clés du langage.
|
||||
|
||||
Le compilateur connaît les règles nécessaires pour typer, vérifier, constant-fold et abaisser ces opérations vers Sase IR, mais l'inventaire exhaustif de la surface API appartient au Core.
|
||||
|
||||
Principe de nommage :
|
||||
L'introduction de `Fault` supprime l'obligation historique de créer une variante `try... -> Result<T,NumericConversionError>` uniquement parce qu'une conversion directe peut échouer.
|
||||
|
||||
Principe de nommage V1 :
|
||||
|
||||
```text
|
||||
toTarget()
|
||||
conversion exacte et totale
|
||||
|
||||
tryToTarget()
|
||||
conversion exacte pour la valeur courante, récupérable si impossible
|
||||
conversion exacte
|
||||
totale si tout le domaine source est représentable
|
||||
sinon valeur directe + faults NumericConversionFault
|
||||
|
||||
roundToTarget()
|
||||
perte de précision explicitement acceptée, conversion totale
|
||||
|
||||
tryRoundToTarget()
|
||||
perte de précision explicitement acceptée, mais conversion pouvant échouer
|
||||
perte de précision / arrondi explicitement accepté
|
||||
peut fault si une autre précondition reste violée, par exemple le domaine fini destination
|
||||
|
||||
saturateToTarget()
|
||||
saturation explicite lorsqu'aucun arrondi supplémentaire n'est nécessaire
|
||||
saturation explicite lorsque la politique de plage est le seul ajustement demandé
|
||||
|
||||
saturatingRoundToTarget()
|
||||
arrondi + saturation explicitement annoncés
|
||||
arrondi + saturation explicitement annoncés lorsque les deux sont nécessaires
|
||||
|
||||
wrapToTarget()
|
||||
wrapping entier explicite
|
||||
```
|
||||
|
||||
Une forme n'existe que si elle apporte une sémantique observable différente d'une autre forme déjà disponible pour la paire source/destination.
|
||||
Une opération n'existe que si elle apporte une sémantique observable différente d'une autre forme disponible pour la paire source/destination.
|
||||
|
||||
Ainsi, lorsqu'un `toTarget()` exact et total existe, les variantes `tryToTarget()`, `saturateToTarget()` ou `wrapToTarget()` qui produiraient exactement le même résultat pour tout le domaine source sont absentes.
|
||||
Le Core ne duplique pas mécaniquement :
|
||||
|
||||
```text
|
||||
toTarget()
|
||||
tryToTarget()
|
||||
```
|
||||
|
||||
lorsque la seule différence serait que la seconde transporte le même échec dans `Result`.
|
||||
|
||||
Une API spécialisée peut toujours choisir volontairement `Result` si l'échec doit devenir une donnée normale du domaine, mais cela ne fait pas partie de la matrice canonique des conversions primitives.
|
||||
|
||||
Règle de composition : deux opérations Core ne sont fusionnées dans un même nom que si leur séparation en opérations successives modifierait la sémantique, perdrait de l'information ou empêcherait d'exprimer le même contrat.
|
||||
|
||||
Lorsqu'une composition existante est strictement équivalente, aucun alias combiné n'est ajouté au Core.
|
||||
|
||||
## 24.6 `float -> integer` — V1 REQUIS — DIRECTION FIGÉE
|
||||
## 24.6 Entier -> entier — V1 REQUIS — FIGÉ EN PRINCIPE
|
||||
|
||||
Un flottant ne possède jamais un simple `toIntXX()` ou `toUintXX()`.
|
||||
|
||||
La conversion exacte stricte utilise :
|
||||
Si toutes les valeurs source sont représentables exactement dans la destination :
|
||||
|
||||
```text
|
||||
tryToIntXX()
|
||||
tryToUintXX()
|
||||
toTarget() -> Target
|
||||
```
|
||||
|
||||
Elle réussit uniquement si la valeur source est finie, mathématiquement entière et représentable exactement dans le type destination. Sinon elle retourne `Result::Err(NumericConversionError(...))`.
|
||||
est total et ne déclare aucun `NumericConversionFault` lié à la plage.
|
||||
|
||||
Les politiques mathématiques de traitement de la partie fractionnaire restent des opérations Core séparées :
|
||||
Lorsque certaines valeurs source ne sont pas représentables :
|
||||
|
||||
```text
|
||||
toTarget() -> Target
|
||||
faults NumericConversionFault
|
||||
```
|
||||
|
||||
avec `OutOfRange` lorsque la valeur courante n'entre pas dans le domaine destination.
|
||||
|
||||
Les politiques alternatives restent explicites :
|
||||
|
||||
```text
|
||||
saturateToTarget()
|
||||
wrapToTarget()
|
||||
```
|
||||
|
||||
Elles ne sont présentes que lorsqu'elles peuvent produire un résultat différent de `toTarget()` pour cette paire.
|
||||
|
||||
Aucun wrapping ni saturation n'est implicite.
|
||||
|
||||
## 24.7 Entier -> flottant — V1 REQUIS — FIGÉ EN PRINCIPE
|
||||
|
||||
`toFloatXX()` exige une représentation exacte de la valeur entière.
|
||||
|
||||
```text
|
||||
toFloatXX() -> floatXX
|
||||
faults NumericConversionFault
|
||||
```
|
||||
|
||||
n'a une clause `faults` que lorsque certaines valeurs source peuvent être inexactes ou hors du domaine fini destination.
|
||||
|
||||
`roundToFloatXX()` accepte explicitement l'arrondi canonique `nearest, ties to even` :
|
||||
|
||||
```text
|
||||
roundToFloatXX() -> floatXX
|
||||
```
|
||||
|
||||
Il peut encore produire `OutOfRange` si une valeur entière finie dépasse le domaine fini de la destination.
|
||||
|
||||
Lorsque l'arrondi et la saturation doivent être annoncés ensemble :
|
||||
|
||||
```text
|
||||
saturatingRoundToFloatXX()
|
||||
```
|
||||
|
||||
borne une magnitude finie trop grande au plus grand fini de même signe puis applique la précision destination.
|
||||
|
||||
Il n'existe pas de `wrapToFloatXX()`.
|
||||
|
||||
## 24.8 `float -> integer` — V1 REQUIS — FIGÉ EN PRINCIPE
|
||||
|
||||
La conversion stricte utilise désormais directement :
|
||||
|
||||
```text
|
||||
toIntXX() -> intXX
|
||||
faults NumericConversionFault
|
||||
|
||||
toUintXX() -> uintXX
|
||||
faults NumericConversionFault
|
||||
```
|
||||
|
||||
Elle réussit uniquement si la valeur source est :
|
||||
|
||||
```text
|
||||
finie
|
||||
mathématiquement entière
|
||||
dans le domaine de la destination
|
||||
exactement représentable comme entier destination
|
||||
```
|
||||
|
||||
Sinon le fault porte la catégorie appropriée.
|
||||
|
||||
Les politiques mathématiques de traitement de la partie fractionnaire restent des opérations séparées :
|
||||
|
||||
```text
|
||||
floor()
|
||||
@@ -116,30 +193,38 @@ truncate()
|
||||
Elles se composent avec la conversion :
|
||||
|
||||
```text
|
||||
value::floor()::tryToInt32()
|
||||
value::ceil()::tryToInt32()
|
||||
value::round()::tryToInt32()
|
||||
value::truncate()::tryToInt32()
|
||||
value::floor()::toInt32()
|
||||
value::ceil()::toInt32()
|
||||
value::round()::toInt32()
|
||||
value::truncate()::toInt32()
|
||||
```
|
||||
|
||||
Saselang n'introduit donc pas les alias redondants `tryFloorToInt32()`, `tryCeilToInt32()`, `tryRoundToInt32()` ou `tryTruncateToInt32()` lorsque ces compositions possèdent exactement la même sémantique.
|
||||
Saselang n'introduit pas les alias redondants `floorToInt32()`, `ceilToInt32()`, `roundToInt32()` ou `truncateToInt32()` lorsque ces compositions possèdent exactement la même sémantique.
|
||||
|
||||
Les politiques de dépassement de domaine suivent la même règle :
|
||||
Les politiques de dépassement de domaine se composent de la même façon :
|
||||
|
||||
```text
|
||||
value::floor()::trySaturateToInt32()
|
||||
value::round()::tryWrapToInt32()
|
||||
value::floor()::saturateToInt32()
|
||||
value::round()::wrapToInt32()
|
||||
```
|
||||
|
||||
`trySaturateToIntXX()` exige une valeur finie et mathématiquement entière, puis sature aux bornes du type destination. Les échecs qui ne relèvent pas de la plage, par exemple `NaN`, une infinité ou une valeur fractionnaire, restent des `NumericConversionError`.
|
||||
Pour `float -> integer` :
|
||||
|
||||
`tryWrapToIntXX()` exige également une valeur finie et mathématiquement entière, puis applique le wrapping entier modulo `2^N` défini par Saselang.
|
||||
```text
|
||||
saturateToTarget()
|
||||
exige encore une valeur finie et mathématiquement entière
|
||||
faults NotFinite / NotIntegral si nécessaire
|
||||
sature uniquement la plage
|
||||
|
||||
Une méthode combinée reste admise lorsqu'une décomposition changerait le contrat ou perdrait l'information nécessaire. C'est notamment le cas de `saturatingRoundToFloat32()` pour certains narrowings flottants : un `roundToFloat32()` séparé pourrait échouer avant que la saturation ne puisse être appliquée.
|
||||
wrapToTarget()
|
||||
exige encore une valeur finie et mathématiquement entière
|
||||
faults NotFinite / NotIntegral si nécessaire
|
||||
applique le wrapping modulo 2^N à l'entier mathématique fini
|
||||
```
|
||||
|
||||
Aucun comportement de conversion ne dépend d'un profil, d'un linter ou d'un backend.
|
||||
Une méthode combinée reste admise uniquement lorsqu'une décomposition changerait le contrat ou perdrait l'information nécessaire.
|
||||
|
||||
## 24.7 `float -> float`, valeurs IEEE spéciales — V1 REQUIS — FIGÉ
|
||||
## 24.9 `float -> float`, valeurs IEEE spéciales — V1 REQUIS — FIGÉ
|
||||
|
||||
Toute conversion de valeur flottante préserve les catégories sémantiques suivantes lorsque la destination est un type flottant Saselang :
|
||||
|
||||
@@ -162,30 +247,33 @@ signe du NaN
|
||||
représentation binaire exacte
|
||||
```
|
||||
|
||||
Ces propriétés relèvent d'un éventuel contrat binaire distinct ; `bitcast<T>` ne peut les préserver que lorsque ses propres contraintes, notamment de taille, sont satisfaites.
|
||||
Ces propriétés relèvent d'un contrat binaire distinct.
|
||||
|
||||
`tryToFloatXX()` traite `NaN` et les infinités comme des valeurs sémantiques représentables du type flottant destination. Le mot « exact » désigne ici l'exactitude de la valeur sémantique Saselang, pas l'identité bit-à-bit d'un payload NaN.
|
||||
|
||||
Pour une valeur finie :
|
||||
Pour une réduction de format :
|
||||
|
||||
```text
|
||||
tryToFloatXX()
|
||||
exige une représentation exacte
|
||||
toFloatXX()
|
||||
exige l'exactitude de la valeur sémantique
|
||||
faults Inexact ou OutOfRange pour une valeur finie si nécessaire
|
||||
|
||||
tryRoundToFloatXX()
|
||||
roundToFloatXX()
|
||||
accepte l'arrondi canonique
|
||||
refuse une valeur finie hors domaine fini destination
|
||||
faults OutOfRange si une valeur finie dépasse le domaine fini destination
|
||||
|
||||
saturatingRoundToFloatXX()
|
||||
accepte l'arrondi canonique
|
||||
sature une valeur finie hors domaine vers +/-Target::Max
|
||||
```
|
||||
|
||||
Les infinités ne sont pas saturées puisqu'elles sont déjà des valeurs représentables du format flottant destination.
|
||||
`NaN` et les infinities sont déjà représentables dans les formats flottants Saselang et ne sont donc pas saturés.
|
||||
|
||||
## 24.8 `NumericConversionError` — V1 REQUIS — NOM DE TRAVAIL / CODES FIGÉS
|
||||
## 24.10 `NumericConversionFault` — V1 REQUIS — NOM DE TRAVAIL / CODES FIGÉS
|
||||
|
||||
`NumericConversionError` est retenu comme nom de travail du `ResultError` Core utilisé par les conversions numériques récupérables.
|
||||
`NumericConversionFault` est retenu comme nom de travail du `Fault` Core utilisé par les conversions numériques directes dont les préconditions runtime peuvent échouer.
|
||||
|
||||
```text
|
||||
NumericConversionFault extends Fault
|
||||
```
|
||||
|
||||
Les catégories sémantiques V1 sont :
|
||||
|
||||
@@ -213,7 +301,7 @@ Inexact
|
||||
alors que l'opération exige l'exactitude
|
||||
```
|
||||
|
||||
`NumericConversionError` reste volontairement généraliste et minimal. Il n'ajoute pas automatiquement :
|
||||
`NumericConversionFault` reste volontairement minimal. Il n'ajoute pas automatiquement :
|
||||
|
||||
```text
|
||||
sourceType
|
||||
@@ -224,21 +312,19 @@ backend
|
||||
roundingMode
|
||||
```
|
||||
|
||||
au-delà de l'état commun fourni par `ResultError`.
|
||||
au-delà de l'état commun fourni par `Error`/`Fault`.
|
||||
|
||||
Le nom concret et la représentation numérique interne des codes pourront encore être ajustés avant stabilisation publique, mais les catégories sémantiques ci-dessus font partie du contrat V1.
|
||||
|
||||
## 24.9 Réduction anti-doublon par paire — V1 REQUIS — FIGÉ
|
||||
## 24.11 Réduction anti-doublon par paire — V1 REQUIS — FIGÉ
|
||||
|
||||
La matrice examine chaque paire `Source -> Target` et n'expose que les opérations dont le comportement peut réellement être distingué sur le domaine source.
|
||||
|
||||
Ainsi, pour une paire `float -> integer` dont toutes les valeurs finies mathématiquement entières sont déjà dans le domaine signé destination, `trySaturateToTarget()` et `tryWrapToTarget()` seraient des doublons de `tryToTarget()` et n'existent pas.
|
||||
Lorsqu'une opération directe est totale pour toute la paire, elle ne déclare aucun fault inutile.
|
||||
|
||||
Pour une destination non signée, les valeurs négatives suffisent généralement à rendre les politiques checked, saturating et wrapping distinctes.
|
||||
Lorsqu'une politique `saturate` ou `wrap` ne peut jamais différer de la conversion exacte sur cette paire, elle est absente.
|
||||
|
||||
La matrice exhaustive en annexe est normative sur ce point.
|
||||
La disparition des variantes `try...` ne modifie pas cette règle de non-redondance ; elle supprime seulement les duplications qui ne différaient que par le canal d'échec `ResultError` versus valeur directe.
|
||||
|
||||
## 24.10 Disponibilité Core et target — V1 REQUIS — FIGÉ EN PRINCIPE
|
||||
## 24.12 Disponibilité Core et target — V1 REQUIS — FIGÉ EN PRINCIPE
|
||||
|
||||
Les conversions fondamentales de la matrice sont des capacités Core obligatoires dès lors que les types source et destination sont supportés par le target.
|
||||
|
||||
@@ -248,9 +334,9 @@ Un target réduit peut ne pas supporter un type fondamental donné. Dans ce cas
|
||||
|
||||
Voir également le chapitre 40.
|
||||
|
||||
## 24.11 Matrice exhaustive — ANNEXE NORMATIVE EN COURS DE GEL
|
||||
## 24.13 Matrice exhaustive — ANNEXE NORMATIVE EN COURS DE GEL
|
||||
|
||||
L'inventaire source → destination des opérations de conversion est maintenu séparément afin d'éviter les oublis, doublons et incohérences.
|
||||
L'inventaire source -> destination des opérations de conversion est maintenu séparément afin d'éviter les oublis, doublons et incohérences.
|
||||
|
||||
Voir :
|
||||
|
||||
@@ -258,6 +344,6 @@ Voir :
|
||||
annexes/A-numeric-conversions.md
|
||||
```
|
||||
|
||||
L'annexe doit lister le maximum de possibilités sémantiquement distinctes avant réduction finale de l'API Core.
|
||||
L'annexe liste les possibilités sémantiquement distinctes après suppression des variantes `try...` purement liées à l'ancien canal `ResultError`.
|
||||
|
||||
---
|
||||
|
||||
@@ -13,11 +13,13 @@ threads natifs
|
||||
structured concurrency éventuelle
|
||||
cancellation
|
||||
synchronisation
|
||||
interaction avec Result/throws
|
||||
interaction avec Result/throws/faults
|
||||
interaction avec destruction déterministe
|
||||
runtime minimal
|
||||
```
|
||||
|
||||
Aucune dépendance obligatoire à un executor monolithique ne doit être supposée sans justification.
|
||||
|
||||
Les collections concurrentes spécialisées relèvent du SDK et non d'une symétrie artificielle du Core. Une future `ConcurrentMap<K,V>` doit exprimer des garanties de concurrence/atomicité orthogonales aux capacités structurelles `Map` / `SettableMap` / `ResizableMap`. Des types tels que `ConcurrentHashMap<K,V>` ne seront introduits que lorsqu'un contrat concurrent concret le justifie.
|
||||
|
||||
---
|
||||
|
||||
@@ -66,11 +66,51 @@ unsafe field
|
||||
|
||||
Le caractère dangereux est porté par le type/opération.
|
||||
|
||||
## 27.4 Raw pointers — V1 REQUIS — À FINALISER
|
||||
## 27.4 Pointeurs et accès mémoire bas niveau — V1 REQUIS — À FINALISER CRITIQUE
|
||||
|
||||
Les pointeurs bruts existent explicitement et leurs opérations de dereference/arithmétique autorisées doivent être précisément définies.
|
||||
Le modèle mémoire contient un sous-ensemble dédié aux pointeurs ; il ne doit pas être confondu avec les références de classe ou `Slice<T>`.
|
||||
|
||||
Les noms des types (`Ptr<T>`, `PtrMut<T>` ou autre) restent à figer.
|
||||
La spécification devra distinguer au minimum :
|
||||
|
||||
```text
|
||||
référence de classe
|
||||
accès sûr ordinaire vers un objet
|
||||
|
||||
Slice<T>
|
||||
vue sûre, bornée et contiguë sur un stockage existant
|
||||
|
||||
pointeur Saselang
|
||||
adresse explicite avec contrat de type/mutabilité à définir
|
||||
|
||||
pointeur brut / FFI
|
||||
accès mémoire bas niveau potentiellement unsafe
|
||||
|
||||
function pointer
|
||||
représentation ABI d'une cible callable, distincte des callables/closures ordinaires
|
||||
```
|
||||
|
||||
Les noms exacts des types (`Ptr<T>`, `PtrMut<T>` ou autre) restent à figer.
|
||||
|
||||
Doivent être définis explicitement :
|
||||
|
||||
```text
|
||||
nullabilité et non-null par défaut éventuel
|
||||
const T pointé et constness du pointeur lui-même
|
||||
dereference
|
||||
address-of
|
||||
arithmétique de pointeurs
|
||||
alignement
|
||||
allocation/libération manuelles
|
||||
casts entre pointeurs
|
||||
conversion pointeur <-> entier si elle existe
|
||||
pointeur opaque / équivalent éventuel de void*
|
||||
function pointers
|
||||
FFI C
|
||||
ownership ou absence d'ownership attaché au pointeur
|
||||
interaction avec allocator, lifetime interne et thread safety
|
||||
```
|
||||
|
||||
Le code ordinaire ne doit pas avoir besoin de pointeurs explicites lorsque les références de classe, arrays, slices ou autres abstractions sûres suffisent. Les opérations dangereuses doivent rester dans l'inventaire fermé `unsafe`.
|
||||
|
||||
## 27.5 Allocator — V1 REQUIS — À FINALISER CRITIQUE
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ Les sorties qui déclenchent les `defer` du scope traversé comprennent notammen
|
||||
fin normale
|
||||
return
|
||||
throw
|
||||
Fault propagé
|
||||
break
|
||||
continue
|
||||
emit quittant le scope concerné
|
||||
@@ -46,12 +47,12 @@ La valeur d'un `return` ou d'un `emit` est déterminée avant l'exécution des c
|
||||
|
||||
## 28.2 Unwind et ordre avec `catch` / `finally` — V1 REQUIS — FIGÉ
|
||||
|
||||
Lorsqu'une `Exception` quitte un scope, les `defer` des scopes abandonnés sont exécutés avant l'entrée dans le `catch` correspondant.
|
||||
Lorsqu'une `Exception` ou un `Fault` capturable quitte un scope, les `defer` des scopes abandonnés sont exécutés avant l'entrée dans le `catch` correspondant, sous réserve des détails d'unwind de `Fault` encore à fermer avec le runtime.
|
||||
|
||||
Ordre conceptuel :
|
||||
|
||||
```text
|
||||
throw
|
||||
throw / Fault propagé
|
||||
-> unwind des scopes quittés
|
||||
-> defer de ces scopes, LIFO
|
||||
-> catch correspondant éventuel
|
||||
@@ -73,6 +74,7 @@ Sont interdits dans un `defer` lorsqu'ils quittent le bloc :
|
||||
```text
|
||||
return
|
||||
throw
|
||||
fault
|
||||
break
|
||||
continue
|
||||
emit
|
||||
@@ -80,6 +82,8 @@ emit
|
||||
|
||||
Aucune `Exception` non capturée ne peut sortir indirectement d'un `defer`. Une callable appelée depuis un `defer` et susceptible de lancer une `Exception` doit voir cette exception entièrement gérée à l'intérieur du bloc `defer`.
|
||||
|
||||
Un `fault` explicite est interdit dans un `defer` pour la même raison qu'un `throw` explicite : le cleanup ne doit pas volontairement remplacer une sortie déjà en cours. Un `Fault` dynamique produit indirectement reste possible car il est unchecked ; son interaction exacte avec l'unwind/destruction fait partie du modèle mémoire encore à fermer.
|
||||
|
||||
Un `ResultError` reste une simple valeur : un appel retournant `Result<T,E>` est autorisé dans un `defer`, sous réserve des règles normales de traitement de cette valeur.
|
||||
|
||||
## 28.4 `finally` versus `defer` — V1 REQUIS — FIGÉ
|
||||
@@ -94,4 +98,4 @@ Un `try/finally` sans `catch` est interdit précisément parce que `defer` couvr
|
||||
|
||||
Pas de `errdefer` séparé en V1.
|
||||
|
||||
`catch` gère les chemins d'`Exception`; `defer` gère le cleanup systématique du scope. Un troisième mécanisme serait redondant.
|
||||
`catch` gère les chemins d'`Exception` et de `Fault`; `defer` gère le cleanup systématique du scope. Un troisième mécanisme dédié uniquement à l'échec serait redondant.
|
||||
|
||||
@@ -214,7 +214,7 @@ désactiver les contrôles d'overflow définis par Saselang
|
||||
modifier la sémantique d'un index hors limites
|
||||
modifier l'ordre d'évaluation
|
||||
modifier la représentation sémantique des types
|
||||
changer les règles Result/throws
|
||||
changer les règles Result/throws/faults
|
||||
désactiver les vérifications de sûreté du langage
|
||||
faire varier la validité d'un programme Saselang autrement que par une limite propre au backend/target
|
||||
```
|
||||
|
||||
@@ -12,7 +12,9 @@ Pas de visibilité explicite.
|
||||
|
||||
`main` ne peut pas déclarer `throws`, même s'il retourne `Result`.
|
||||
|
||||
L'environnement top-level ne possède pas d'appelant Saselang capable de l'envelopper dans un `try/catch`.
|
||||
L'environnement top-level ne possède pas d'appelant Saselang capable d'assumer un contrat checked : toute `Exception` doit donc être gérée avant de quitter `main`.
|
||||
|
||||
`main` peut documenter des `Fault` par `faults`, comme toute callable. Un `Fault` non capturé atteint la frontière de l'unité d'exécution et provoque l'échec runtime correspondant.
|
||||
|
||||
## 42.2 `ExitCode` — V1 REQUIS — FIGÉ EN PRINCIPE
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ Les commentaires documentaires sont :
|
||||
/** documentation de bloc */
|
||||
```
|
||||
|
||||
Ils ont la même sémantique documentaire avec deux ergonomies d'écriture différentes. Saselang ne reprend pas la redondance Javadoc/PHPDoc consistant à dupliquer systématiquement la signature via `@param`, `@return` ou `@throws` ; `saseldoc` dérive ces informations du programme.
|
||||
Ils ont la même sémantique documentaire avec deux ergonomies d'écriture différentes. Saselang ne reprend pas la redondance Javadoc/PHPDoc consistant à dupliquer systématiquement la signature via `@param`, `@return`, `@throws` ou `@faults` ; `saseldoc` dérive ces informations du programme.
|
||||
|
||||
Une Saseldoc est autorisée uniquement lorsqu'elle est immédiatement attachée à une déclaration documentable. Elle n'est jamais un commentaire libre à l'intérieur d'un corps exécutable.
|
||||
|
||||
@@ -128,7 +128,7 @@ Un marqueur `@...` inconnu dans un commentaire documentaire ne doit pas faire é
|
||||
|
||||
La visibilité n'est pas réduite artificiellement à un ordre total lorsque les domaines `protected`, `module` et `package` ne sont pas sémantiquement comparables. L'interface de filtrage doit pouvoir exprimer les ensembles de visibilité nécessaires sans falsifier le modèle d'accès du langage.
|
||||
|
||||
Les signatures, types de paramètres, generics, visibilité, `Result`, `throws`, héritage et interfaces sont dérivés du modèle sémantique plutôt que redéclarés manuellement dans la Saseldoc.
|
||||
Les signatures, types de paramètres, generics, visibilité, `Result`, `throws`, `faults`, héritage et interfaces sont dérivés du modèle sémantique plutôt que redéclarés manuellement dans la Saseldoc.
|
||||
|
||||
Les formats exacts V1 restent à finaliser avec la CLI, mais un format de sortie n'implique jamais un outil séparé.
|
||||
|
||||
|
||||
@@ -41,16 +41,17 @@ Restent à fermer :
|
||||
|
||||
## 48.4 Erreurs
|
||||
|
||||
Points désormais largement figés : hiérarchie `Error` / `ResultError` / `Exception`, `Result::Ok` / `Result::Err`, contraintes de `Result<T,E>`, absence de `unwrap` et `?` V1, `throw` / `throws`, contrats d'override/interface, sélection ordonnée des `catch`, `finally` non-escaping, causes, i18n, code de `ResultError`, stack trace d'`Exception` liée au `throw`, et sémantique LIFO/non-escaping de `defer`.
|
||||
Points désormais largement figés : hiérarchie `Error` / `ResultError` / `Exception` / `Fault`, `Result::Ok` / `Result::Err`, contraintes de `Result<T,E>`, absence de `unwrap` et `?` V1, `throw` / `throws` pour les exceptions checked, `fault` / `faults` pour les faults unchecked, `catch` limité aux branches `Exception` et `Fault`, contrats d'override `throws`, causes, i18n, code de `ResultError`, stack trace diagnostique et sémantique LIFO/non-escaping de `defer`.
|
||||
|
||||
Restent à fermer :
|
||||
|
||||
- faute runtime/panic/fatal error ;
|
||||
- interaction exacte cleanup/destruction/unwind pendant un `Fault` ;
|
||||
- unité d'exécution exacte terminée par un `Fault` non capturé ;
|
||||
- constructors faillibles et construction partielle ;
|
||||
- interaction exacte cleanup/destruction pendant fault ;
|
||||
- ordre exact `defer` / destructeurs automatiques avec le modèle mémoire ;
|
||||
- représentation Core/runtime exacte de `I18nMessage`, `ResultErrorCode`, `StackTrace` et `StackFrame` ;
|
||||
- éventuels helpers Core futurs `expectOk` / `expectErr` seulement si un besoin réel est démontré.
|
||||
- politique de lint/documentation pour les `Fault` explicites non listés dans `faults` ;
|
||||
- éventuels helpers Core futurs de `Result` seulement si un besoin réel est démontré.
|
||||
|
||||
## 48.5 Contrôle de flux
|
||||
|
||||
@@ -97,7 +98,10 @@ Restent à fermer :
|
||||
## 48.9 Core/SDK
|
||||
|
||||
- frontière Core/SDK définitive ;
|
||||
- collections V1 ;
|
||||
- `Vector<T>` et interaction avec `Slice<T>` lors des reallocations ;
|
||||
- contrats `Hashable` / égalité et implémentations HashSet/HashMap ;
|
||||
- opérations avancées de bornes/ranges pour `SortedSet` / `SortedMap` ;
|
||||
- propagation formelle de `const` dans `View<T>`, `Option<T>`, `Iterator<T>` et autres wrappers ;
|
||||
- String encodings ;
|
||||
- regex ;
|
||||
- filesystem/network/process ;
|
||||
|
||||
@@ -13,8 +13,8 @@ Les futures décisions doivent respecter les invariants suivants :
|
||||
9. **Les types Core compiler-known restent peu nombreux.**
|
||||
10. **Le package, le namespace, le target, la feature, la capability et l'artifact restent des dimensions distinctes.**
|
||||
11. **Une collision de namespace entre packages ne doit jamais fusionner implicitement leurs types.**
|
||||
12. **Les erreurs récupérables utilisent `Result`; les opérations réellement infaillibles retournent directement leur valeur.**
|
||||
13. **`throws` n'existe que sur une callable retournant `Result`.**
|
||||
12. **`ResultError`, `Exception` et `Fault` sont trois mécanismes distincts : valeur d'échec explicite, propagation checked et échec unchecked capturable.**
|
||||
13. **`throws` ne contient que des `Exception` et reste indépendant du type de retour ; `faults` ne contient que des `Fault` et reste optionnel/non exhaustif.**
|
||||
14. **L'identité d'objet est distincte de l'égalité de valeur.**
|
||||
15. **Les opérateurs utilisateur passent uniquement par les contrats Core `Op...`.**
|
||||
16. **Les interfaces fournissent l'héritage multiple de contrats/comportements sans introduire un second système de traits.**
|
||||
@@ -34,5 +34,6 @@ Les futures décisions doivent respecter les invariants suivants :
|
||||
29. **Un array safe devient observable uniquement après initialisation complète.**
|
||||
30. **Une bibliothèque spécialisée peut être officielle sans appartenir au Core ou au SDK obligatoire ; son artifact normal reste une `.saselib`.**
|
||||
31. **Les dépendances natives de bootstrap peuvent être remplacées progressivement par des implémentations Saselang sans imposer leur architecture historique au langage.**
|
||||
32. **Lorsqu'une forme légèrement plus longue supprime un implicite ou une ambiguïté réelle, Saselang privilégie l'explicite.**
|
||||
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user