4.4. Modèles de mémoire
Le chapitre 1 (§1.3) a posé que l'absence de course à la donnée et la sûreté
mémoire se déduisent de l'absence de diagonale dans C, sans le démontrer. C'est ici que la
démonstration a son lieu, puisque les deux objets qu'elle met en jeu — la fibrille et l'arène —
viennent d'être construits. La capacité d'écriture \mathsf{WriteCap}(r) est une ressource
linéaire au sens du chapitre 3 (§3.1), et l'énoncé n'emploie de son
grade que le caractère linéaire.
Un modèle dénotationnel existe pour cette discipline. Il est situé ici plutôt qu'adopté. Un espace de capacité est un ensemble muni d'une relation de poids qui assigne à chaque valeur les ensembles de capacités qu'elle peut détenir. Un morphisme y est une fonction qui préserve les poids, et ses auteurs établissent que ce sont précisément les fonctions sûres du point de vue des capacités, sans accès non autorisé ni autorité ambiante [22]. Ce modèle valide la discipline que ce document emploie, non son énoncé : son objet est la permission de produire un effet, quand celui du théorème est la disjonction de régions. Et il sert un système qui recouvre la pureté depuis un ambiant impur, direction que ce document écarte ; l'adopter comme sémantique importerait la lecture relative de la pureté qu'il refuse.
Soient t_1 et t_2 deux membres du multi-ensemble de calculs, composés par la règle Par
(§3.6.4.4) et donc sous des contextes additionnés,
\Delta_1 + \Delta_2. On suppose (H1) l'unicité d'introduction : la règle d'introduction de
\mathsf{WriteCap}(r) consomme linéairement l'arène ou le segment dont elle découpe r, de sorte
qu'au plus une capacité d'écriture par région est dérivable en contexte clos ; (H2) la portée :
deux capacités de \mathsf{Range} disjoints ne dénotent pas la même région (arithmétique
d'intervalles, déchargeable par le solveur) ; et (H3) le respect du sens d'imbrication des
délimiteurs du chapitre 5, qui interdit à une valeur cartésienne de capturer une capacité
linéaire. Soit \mathsf{WriteCap}(r) une capacité d'écriture sur une région d'arène
r. Si \Delta_1 \vdash t_1 : \mathsf{WriteCap}(r) \multimap \mathsf{Unit}, alors il n'existe
aucun terme t_2' tel que \Delta_2 \vdash t_2' : \mathsf{WriteCap}(r) \multimap \tau, quel que
soit \tau : aucune opération mutante sur r n'est typable sous \Delta_2.
Instance du lemme de capacité (chapitre 2, §2.6,
théorème 21), la ressource étant la région r et la capacité
\mathsf{WriteCap}(r). L'absence de diagonale interdit de dupliquer une capacité donnée ; elle
n'interdit pas d'en introduire deux pour la même région, et c'est (H1) qui l'exclut, (H2)
ramenant la disjonction des régions à celle des intervalles. Sans (H1), une primitive
alloc_range non linéaire en son arène produirait deux capacités distinctes pour la même région :
le théorème tiendrait pour chacune et tomberait pour le couple. Celle-ci vit dans le fragment linéaire strict de C, lequel ne porte par
construction aucun morphisme de duplication A \to A \otimes A. C'est l'addition des contextes
qui porte la disjonction, et c'est ce que la règle Par donne : une capacité de grade 1
présente dans \Delta_1 + \Delta_2 y est présente une seule fois, la somme des grades valant 1
et non 2. Sa consommation par t_1 la retire donc structurellement de \Delta_2, et le
raisonnement vaut sur le multi-ensemble entier par associativité de l'addition. Sans elle dans son
propre contexte de typage, t_2 ne peut construire aucun terme bien typé opérant sur r :
l'absence se lit sur la dérivation, sans analyse supplémentaire.
Une hypothèse porte tout, et elle n'est pas gratuite : (H3). Les deux autres sont des lemmes sur la règle d'introduction ; la « région » s'y entend comme une discipline de portée, que le polymorphisme paramétrique ordinaire suffit à définir. RMQ 49. C'est le sens unique d'imbrication des
délimiteurs qui tient l'hypothèse. Ce théorème dit ce qui casse dans l'autre sens. La preuve
suppose les deux contextes disjoints, ce que \Gamma_1 \otimes \Gamma_2 écrit mais ne garantit pas
par lui-même dès que les fragments s'imbriquent. La couche 3 admet la contraction ; si une valeur
cartésienne pouvait capturer une capacité linéaire, la duplication licite en couche 3 dupliquerait
une ressource qui ne doit pas l'être, et l'énoncé tomberait. La logique adjointe donne le
contre-exemple sous forme courte et la parade avec : c'est la déclaration d'indépendance entre
modes, sans laquelle la contraction du mode le plus permissif fuit vers le mode le plus contraint [23].
Le chapitre 5 (§5.1) justifie le sens unique d'imbrication
des délimiteurs par l'inclusion des contextes ; ce qu'il ne montre pas est ce qui casse dans l'autre
sens, et c'est ce théorème.
S'il tient, l'absence de course à la donnée exigée par P4 et la sûreté mémoire exigée par P3 cessent d'être des propriétés à vérifier sur le langage : ce sont deux lectures de l'absence de diagonale dans C. S'il tombe — si la déclaration d'indépendance entre modes n'était pas tenable —, il faudrait un mécanisme d'exécution pour interdire la mutation concurrente, c'est-à-dire ce que le postulat d'autonomie physique refuse.
Dans un contexte clos, la règle d'introduction d'une ressource d'écriture — capacité sur un segment
d'arène, destination, grade linéaire — consomme linéairement l'objet qu'elle découpe, et son indice
(taille du segment, âge k de \mathsf{Lin}_k, taille de l'arène) décroît strictement. Il en
résulte : (a) au plus une capacité d'écriture par région est dérivable (hypothèse H1 du théorème
37) ; (b) les destinations ne forment pas de cycle ; (c) la
construction d'une arène termine.
Les trois conséquences sont une seule loi lue sur trois objets. La consommation linéaire de l'objet
découpé interdit d'en tirer deux capacités ; la décroissance stricte de l'indice interdit qu'une
capacité redevienne l'ancêtre de la région qui la porte, d'où l'absence de cycle ; elle est enfin la
mesure qui fonde la terminaison des catamorphismes (chapitre 3, §3.1).
Le lemme de portée (H2) se démontre : deux segments [a,b] et [c,d] d'une même arène sont
disjoints exactement lorsque b < c ou d < a, formule de l'arithmétique linéaire que le solveur
décharge ; des cellules d'indices distincts étant des régions distinctes, deux capacités de
\mathsf{Range} disjoints ne dénotent pas la même région. L'unicité (H1) se lit sur la règle Slice (§3.6) : une capacité sur un segment ne naît que de l'élimination de l'arène, linéaire en l'arène, ou de la découpe d'une capacité détenue, qui la consomme ; par induction sur la dérivation, deux capacités sur un même segment en contexte clos exigeraient deux consommations de la même ressource linéaire, impossible sans diagonale. Reste à écrire l'élimination de l'arène, exception déclarée du jeu de règles : (H1) en dépend.
Ce que les arènes viennent de faire pour l'acteur, la mémoire physique le fait sur sept niveaux, et c'est ici qu'il faut le dire puisque la section précédente vient d'en poser le cas principal. Cette hiérarchie instancie l'exigence d'effacement que le chapitre 3 (§3.1) pose pour les grades ; aucun de ses niveaux ne s'appuie sur un ramasse-miettes ou un comptage de références atomique.
Les sept niveaux de gestion mémoire de K7PL
Niveau | Mécanisme | Usage | Couche | Coût |
|---|---|---|---|---|
1 | Pointeurs tagués | Encodage direct des scalaires dans le mot machine | L2/L3 |
|
2 | Allocation sur la pile | Liaisons locales non-échapantes | L3 |
|
3 | Régions scopées |
Arènes temporaires bornées par un grade | L2/L3 |
|
4 | Tas avec ownership | Données mutables (transfert linéaire/affin) | L2 |
|
5 | Déduplication canonique | Graphes immuables partagés (hash-consing BLAKE3) | L1 |
|
6 | Arènes linéaires contiguës | Données SoA pour ECS | L1 |
|
7 | Mémoire linéaire WAT | Interfaçage physique brut (FFI, DMA) | L1 |
|
Le cinquième régime appelle une précision que le tableau ne peut pas porter, et dont la réponse n'est pas indifférente. La déduplication canonique est un partage de graphe ; reste à dire si ce partage est observable depuis le langage. La question n'est pas oiseuse : observer que deux sous-termes sont le même objet, et non seulement égaux, c'est observer la représentation et non la valeur, et toute solution directe y perd la transparence référentielle [24].
Deux réponses sont possibles et il faut en choisir une. K7PL retient la première — le partage n'est pas observable, la déduplication est une propriété de la représentation et rien du langage ne permet de la constater —, de sorte que la transparence est préservée sans condition. La seconde réponse resterait ouverte si le besoin s'en faisait sentir : rendre le partage observable sous une modalité, la transparence étant alors préservée partout où la modalité est absente. C'est une possibilité que les grades donnent et que la littérature citée, qui n'en a pas, ne pouvait pas envisager.
Deux travaux plus récents rendent cette seconde voie moins spéculative. L'un traite le partage et la mutation par des coeffets, c'est-à-dire par l'appareil même de ce document [25]~; l'autre établit que le partage maximal se décide, ce qui borne ce qu'un compilateur peut promettre [26]. Ce chapitre n'emprunte ni l'un ni l'autre — son choix reste que le partage n'est pas observable —, mais l'ouverture qu'il laisse n'est pas une porte sur du vide.