Introduction
Bienvenue sur le guide de la syntaxe transcendantale.
Ce guide présente les idées de Jean-Yves Girard sous le point de vue d'un informaticien.
Notez que ce guide est très expérimental et est suceptible de changer régulièrement. Le langage présenté est tout aussi instable.
Je suis intéressé par toutes opinions, recommandations, suggestions ou simples questions. N'hésitez pas à me contacter à boris.eng@proton.me.
Bonne lecture !
Syntaxe transcendantale
La logique vit habituellement dans des systèmes logiques tous plus ou moins compatibles entre eux. On peut parler de pluralisme logique.
Les logiques fonctionnent habituellement avec une base calculatoire appelée syntaxe qui suit les règles dictées par une autre syntaxe de plus haut niveau qu'on appelle sémantique. En particulier, sont imposés : les objets utilisables, leur forme, leurs interactions possibles et les formules ou spécifications que l'on peut exprimer.
Cette action à distance entre deux espaces hermétiques, bien que le fruit de notre perception d'une certaine structuration de la réalité, est un frein à la manipulation et à la compréhension des mécanismes logiques. La réalité logique en tant que telle n'est cependant pas directement accessible (comment le serait-elle ?).
Nous ne pouvons reposer notre compréhension de la logique sur des considérations arbitraires en apparence, bien qu'effectives. Il faut savoir justifier leur pertinence et les recontextualiser voire de les naturaliser (et peut-être ainsi obtenir une science de la logique ?). La syntaxe transcendantale, conçue par Jean-Yves Girard, propose :
- une réorganisation des concepts du calcul et de la logique pour mettre en évidence leurs distinctions et leurs interactions;
- une rétro-ingénierie de la logique à partir du calcul syntaxique permettant de replacer nos connaissances dans un nouvel environnement logique;
- une justification calculatoire des principes logiques.
Exemple en théorie des automates
Un automate fini est une machine qui peut lire un mot (lettre par lettre et sans mémoire) puis l'accepter ou non. Un automate peut être caractérisé par l'ensemble de ses mots acceptés formant son langage reconnu.
Si nous faisons face à un automate quelconque, comment déterminer quel est son langage reconnu ? Il y a plusieurs possibilités :
- analyser l'automate en l'évaluant et en le jugeant (option sémantique que l'on souhaite éviter);
- regarder sa réaction face à tous les mots (option rationnelle mais irréaliste). Ce que Girard appelle le sens d'usage;
- plonger la représentation d'automate et de mot dans un autre espace interactionnel plus large dans lequel on pourrait tester (de façon effective) la réaction de l'automate face à un nombre fini d'objets "exotiques" permettant de déterminer son langage reconnu. Ce que Girard appelle le sens d'usine.
Résolution stellaire
La résolution stellaire est le langage (théorique) utilisé dans la syntaxe transcendantale. Il s'agit d'un langage fonctionnant par interaction asynchrone d'agents indépendants. Cette interaction se base sur un algorithme d'unification (celui qui intervient dans le langage Prolog) servant de noyau minimal du calcul.
Il représente le calcul par un réseau de contraintes syntaxiques qui doivent être résolues pour propager de l'information. Le résultat du calcul correspond aux informations laissées par les réseaux qui sont restés cohérents jusqu'au bout sans rencontrer de conflits.
Ce sont des idées qui existent déjà en programmation logique mais qui sont reformulées et utilisées différemment. En particulier, on n'attache aucun sens logique aux objets (même si on peut les voir comme tel, ils ne sont pas censés représenter des relations ou des objets de systèmes logiques).
Ce calcul peut être compris de plusieurs manières. C'est :
- un calcul de processus;
- un langage de programmation par contraintes;
- une variante des clauses disjonctives avec règle de résolution de Robinson;
- une généralisation non planaire des tuiles de Wang ou des abstract tile assembly models (aTAM);
- une généralisation des tuiles flexibles de Jonoska utilisés en calcul ADN;
- une généralisation des briques LEGO;
- un langage de bas niveau exprimant le sens logique;
- une boîte à outils pour construire des types/formules;
- une généralisation des structures de preuves de Jean-Yves Girard;
- une forme de réseaux d'interaction de Lafont ne reposant sur aucune règle autre que les principes d'unification de termes.
Language Stellogen
Le language Stellogen est le langage de programmation utilisé pour définir, manipuler, et faire interagir des entités de la résolution stellaire et ainsi illustrer les principes de syntaxe transcendantale.
Il s'agit d'une réalisation informatique possible de la résolution stellaire et de la syntaxe transcendantale qui fait des choix de conception pour s'adapter aux pratiques de programmation.
Il possède un interpréteur écrit en OCaml ainsi qu'un moteur d'exécution pour la résolution stellaire (qui est un langage indépendant) appelé "Large Star Collider" (LSC), lui aussi écrit en OCaml.
Si la résolution stellaire est un langage de briques élémentaires (comme les LEGO) alors :
- la syntaxe transcendantale est la théorie des briques;
- le Large Star Collider est le principe permettant la connectique des briques;
- Stellogen est un langage permettant de manipuler ces briques pour faire des constructions avancées qu'il serait trop pénibles de faire à la main.
Stellogen est le langage que nous allons explorer pas-à-pas dans ce petit guide.
Démarrer
Pour installer l'interprêteur Stellogen, aller sur ce dépôt git :
https://github.com/engboris/stellogen
Vous pouvez soit compiler à partir des sources ou alors télécharger un exécutable pré-compilé :
- Pour Linux x86-x64: https://github.com/engboris/stellogen/releases
Vous pouvez ensuite simplement écrire vos programmes dans n'importe quel fichier texte et suivre les instructions dans le fichier README.md du dépôt git pour exécuter vos programmes.
Pour un aperçu rapide, n'hésitez pas à explorer :
Si vous êtes prêts, allons-y !
Unification de termes
Syntaxe
Dans la théorie de l'unification, nous écrivons des termes. Ce sont soit :
- des variables (commençant par une majuscule comme
X
,Y
ouVar
); - des fonctions de la forme
f(t1, ..., tn)
oùf
est un symbole de fonction (commençant par une minuscule ou un chiffre) et les expressionst1
, ...,tn
sont d'autres termes.
Tous les identifiants (que cela soit pour les variables ou symboles de
fonction) peuvent contenir les symboles _
, ?
et terminer par une séquence
de symboles '
. Par exemple : x'
, X''
, X_1
.
Exemples de termes.
X
,f(X)
,h(a, X)
,parent(X)
,add(X, Y, Z)
.
On peut aussi omettre la virgule séparant les arguments d'une fonction étant
donné que leur absence de produit pas d'ambiguïté. On écrirait donc
h(a X)
à la place de h(a, X)
.
Principe
Dans la théorie de l'unification, on dit que deux termes sont unifiables
lorsqu'il existe une substitution des variables les rendant identiques.
Par exemple, f(X)
et f(h(Y))
sont unifiables avec la substitution
{X:=h(Y)}
remplaçant X
par h(Y)
.
Les substitutions qui nous intéressent sont celles qui sont les plus générales.
Nous aurions pu considérer la substitution {X:=h(c(a)); Y:=c(a)}
, tout aussi
valide mais inutilement précise.
Une autre façon de voir les choses est de voir cela comme brancher des termes ensemble pour vérifier s'ils sont compatibles se branchent correctement :
-
une variable
X
se branche avec tout ce qui ne contient pasX
comme sous-terme, sinon nous aurions une dépendance circulaire, comme entreX
etf(X)
; -
une fonction
f(t1, ..., tn)
est compatible avecf(u1, ..., un)
oùti
est compatible avecui
pour touti
. -
f(X)
etf(h(a))
sont compatibles avec{X:=h(a)}
; -
f(X)
etX
sont incompatibles; -
f(X)
etg(X)
sont incompatibles; -
f(h(X))
etf(h(a))
sont compatibles avec{X:=a}
.
Etoiles et constellations
Rayons
Le langage de résolution stellaire étend la notion de terme avec la notion de rayon. Un rayon est un terme où les symboles de fonction sont polarisés avec :
+
(polarité positive);-
(polarité négative);- rien (polarité nulle ou absence de polarité).
La compatibilité entre les rayons suit les mêmes règles que l'unification des termes sauf que :
- au lieu de demander l'égalité
f = g
des symboles de fonction lorsquef(t1, ..., tn)
est confronté àg(u1, ..., un)
, on demande quef
etg
aient une polarité opposée (+
contre-
); - les symboles sans polarités ne peuvent pas interagir.
Par exemple :
+f(X)
et-f(h(a))
sont compatibles avec{X:=h(a)}
;f(X)
etf(h(a))
sont incompatibles;+f(X)
et+f(h(a))
sont incompatibles;+f(+h(X))
et-f(-h(a))
sont compatibles avec{X:=a}
;+f(+h(X))
et-f(-h(+a))
sont compatibles avec{X:=+a}
.
Etoiles
Avec les rayons, nous pouvons former des étoiles. Ce sont des collections non ordonnées de rayons:
+f(X) -f(h(a)) +f(+h(X))
L'étoile vide est notée []
.
Constellations
Les constellations sont des séquences non ordonnées d'étoiles séparées
par le symbole ;
et se terminant par un .
:
+f(X) X; +f(+h(X) a f(b)).
La constellation vide est notée {}
.
Les constellations sont en fait les plus petits objets que nous pourront manipuler. C'est elles qui pourront être exécutées comme on exécute un programme.
Exécution de constellations
Fusion d'étoiles
Les étoiles interagissent entre elles par une opération appelée fusion (que l'on pourrait qualifier de protocole d'interaction), en utilisant le principe de la règle de résolution de Robinson.
La fusion entre deux étoiles
r r1 ... rk
et
r' r1' ... rk'
le long de leurs rayons r
et r'
est définie par une nouvelle étoile :
theta(r1) ... theta(rk) theta(r1') ... theta(rk')
où theta
est la substitution la plus générale induite par r
et r'
.
Remarquez que:
r
etr'
sont annihilées pendant la fusion;- les deux étoiles fusionnent;
- la substitution obtenue par résolution du conflit entre
r
etr'
est propagée aux rayons adjacents.
Exemple. La fusion entre
X +f(X)
et-f(a)
fait interagir+f(X)
et-f(a)
ensemble pour propager la substitution{X:=a}
sur l'unique voisinX
. Le résultat est doncX{X:=a}
soita
.
Cette opération de fusion correspond à la règle de coupure pour la logique du premier ordre (correspondant aussi à la règle de résolution). Cependant, la différence ici est que nous sommes dans un cadre "alogique" (nos objets ne portent aucun sens logique).
Exécution
Cela marche à peu près comme pour la résolution d'un puzzle ou un jeu de fléchettes. Vous avez une constellation faites de plusieurs étoiles. Choisissez des étoiles initiales puis des copies des autres étoiles seront jetées dessus pour interagir par fusion. L'opération continue jusqu'à qu'il n'y ait plus d'interactions possibles (saturation). La constellation obtenue à la fin est le résultat de l'exécution.
Plus formellement, nous séparons une constellation en deux parties pour l'exécuter :
- l'espace d'états, un espace d'étoiles qui seront cibles de l'interaction;
- l'espace d'actions qui sont des étoiles qui vont interagir avec les étoiles de l'espace d'états.
On pourrait représenter cette séparation ainsi avec l'espace d'actions
à gauche et l'espace d'états à droite, séparées avec le symbole |-
:
a1 ... an |- s1 ... sm
L'exécution procède de la façon suivante :
- selectionner un rayon
r
d'une étoile d'états
; - chercher toutes les connexions possibles avec des rayons
r'
d'étoiles d'actiona
; - dupliquer
s
à droite pour chaque tel rayonr'
trouvé car il y a plusieurs interactions possibles à satisfaire; - remplacer chaque copie de
s
par la fusion entrea
ets
; - répéter jusqu'à qu'il n'y a plus aucune interaction possible l'espace d'états.
Exemple
Considérons l'exécution de la constellation suivante où l'unique étoile
initiale de l'espace d'état est préfixée par @
:
+add(0 Y Y);
-add(X Y Z) +add(s(X) Y s(Z));
@-add(s(s(0)) s(s(0)) R) R.
Pour l'exemple, on entoure par >>
et <<
les rayons sélectionnés. Nous avons
donc les étapes suivantes :
+add(0 Y Y);
-add(X Y Z) +add(s(X) Y s(Z));
@-add(s(s(0)) s(s(0)) R) R.
Nous avons la séparation suivante :
+add(0 Y Y);
-add(X Y Z) +add(s(X) Y s(Z))
|-
-add(s(s(0)) s(s(0)) R) R
Sélectionnons le premier rayon :
+add(0 Y Y);
-add(X Y Z) +add(s(X) Y s(Z))
|-
>>-add(s(s(0))<< s(s(0)) R) R
Il ne peut pas se connecter à +add(0 Y Y)
car le premier argument 0
est
incompatible avec s(s(0))
. Cependant, il peut interagir avec
+add(s(X) Y s(Z))
. Nous effectuons une fusion suivante entre
-add(X Y Z) +add(s(X) Y s(Z))
et
-add(s(s(0)) s(s(0)) R) R
donnant la substitution {X:=s(0), Y:=s(s(0)), R:=s(Z)}
et le résultat :
-add(s(0) s(s(0)) Z) s(Z)
Nous obtenons donc l'étape suivante :
+add(0 Y Y);
-add(X Y Z) >>+add(s(X) Y s(Z))<<
|-
-add(s(0) s(s(0)) Z) s(Z)
Nous resélectinnons le premier rayon :
+add(0 Y Y);
-add(X Y Z) +add(s(X) Y s(Z))
|-
>>-add(s(0) s(s(0)) Z)<< s(Z)
Il peut se connecter à +add(s(X) Y s(Z))
avec comme substitution
{X:=0, Y:=s(s(0)), Z:=s(Z')}
:
+add(0 Y Y);
-add(X Y Z) +add(s(X) Y s(Z))
|-
-add(0 s(s(0)) Z') s(s(Z'))
Nous sélectionnons le premier rayon :
+add(0 Y Y);
-add(X Y Z) +add(s(X) Y s(Z))
|-
>>-add(0 s(s(0)) Z')<< s(s(Z'))
Il ne peut interagir qu'avec la première étoile d'action +add(0 Y Y)
, ce
qui nous donne :
+add(0 Y Y);
-add(X Y Z) +add(s(X) Y s(Z))
|-
s(s(s(s(0))))
Le résultat de l'exécution est donc :
s(s(s(s(0))))
Exercices
Donner le résultat de l'exécution pour les constellations ci-dessous. Vous pouvez vérifier en écrivant le code suivant dans un fichier :
print <constellation>
puis en l'exécutant.
Par exemple :
print X +f(X); @-f(a).
@+f(X); -f(a).
@+f(X); -f(Y) a.
@+f(X) X; -f(a); -f(b).
@+1(X) -2(X); -2(X) +3(X).
@-1(X) +2(X); -2(X) +1(X).
@-1(X) +2(X); -2(X) +1(X).
@-f(X) X; +f(+g(a)); -g(X) X.
Définir des constellations
Commentaires
Les commentaires commencent par '
et sont écrits entre '''
pour
les commentaires multi-lignes. Ils sont ignorés pendant l'exécution.
Définitions statiques
On peut nommer des constellations que l'on écrit directement. Les identifiants suivent les mêmes règles que pour les symboles de fonction des rayons.
Les constellations sont entourées d'accolades { ... }
.
w = { a }.
x = { +a; -a b }.
z = { -f(X) }.
On peut aussi choisir d'associer un identifiant à un autre :
y = x.
Les accolades autour des constellations peuvent être omises lorsqu'il n'y a pas d'ambiguïté avec les identifiants :
w = { a }.
x = +a; -a b.
z = -f(X).
Comme pour l'application en programmation fonctionnelle, l'union de constellations est notée avec un espacement :
union1 = x y z.
On peut aussi ajouter des parenthèses autour des expressions pour plus de lisibilité ou pour éviter des ambiguïtés syntaxiques :
union1 = (x y) z.
Mais il faut noter que contrairement à la programmation fonctionnelle, il n'y a pas d'ordre défini.
Les définitions statiques correspondent à la notion "d'objet-preuve" dans la correspondance de Curry-Howard.
Affichage
Pour afficher des constellations, vous pouvez utiliser la commande show
suivit d'une constellation :
show +a; -a b.
La commande show
n'exécute pas les constellations. Si vous voulez vraiment
exécuter la constellation et afficher son résultat, utilisez la commande
print
:
print +a; -a b.
Focus
On peut mettre le focus sur toutes les étoiles d'une constellation en
la préfixant aussi avec @
:
x = +a; -a b.
z = -f(X).
union1 = (@x) z.
Processus de construction
On peut enchaîner des constellations avec l'expression process ... end
:
c = process
+n0(0).
-n0(X) +n1(s(X)).
-n1(X) +n2(s(X)).
end
Cet enchaînement part de la première constellation +n0(0)
considérée comme
initiale. Chaque constellation suivante interagit ensuite avec le résultat
précédent. C'est comme si nous faisions le calcul suivant :
@+n0(0);
-n0(X) +n1(s(X)).
donnant
+n1(s(0)).
puis
@+n1(s(0));
-n1(X) +n2(s(X)).
donnant
+n2(s(s(0))).
C'est ce qui correspond aux tactiques dans des assistants de preuve comme Coq et que l'on pourrait assimiler aux programmes impératifs qui altèrent des représentation d'état (mémoire par exemple).
Les constructions dynamiques correspondent à la notion de "processus-preuve" chez Boris Eng ou de "proof-trace" chez Pablo Donato. Les processus-preuves construisent des objet-preuves (constellations).
Arrêt de processus
Dans le résultat d'une exécution, si l'on représente les résultats par des rayons à polarité nulle, alors les étoiles contenant des rayons polarisés peuvent être interprétés comme des calculs non terminés qui pourraient être effacés.
Pour cela, dans les processus de constructions, nous pouvons utiliser
l'expression spéciale kill
:
c = process
+n0(0).
-n0(X) +n1(s(X)).
-n1(X) +n2(s(X)).
-n2(X) result(X); -n2(X) +n3(X).
kill.
end
print c.
Nous avons utilisé un rayon +n3(X)
pour poursuivre des calculs
si nous souhaitons. Le résultat est stocké dans result(X)
.
Mais si nous souhaitons seulement conserver le résultat et retirer toute
autre possibilité de calcul alors on peut utiliser kill
.
Nettoyage de processus
Il arrive parfois que l'on se retrouve avec des étoiles vides []
dans
les processus. Il est possible de s'en débarrasser avec la commande clean
:
print process
+f(0).
-f(X).
clean.
end
Galaxies
Le langage Stellogen ajoute à la résolution stellaire une entité appelée galaxie.
Une galaxie est soit une constellation soit une structure avec des champs nommés contenant d'autres galaxies.
Plus généralement, Stellogen manipule en réalité des galaxies plutôt que des constellations (qui sont des cas particuliers de galaxies).
En Stellogen, "tout est galaxie" de la même manière que tout est objet dans des langages de programmation comme Smalltalk ou Ruby.
On définit les galaxies avec des blocs galaxy ... end
contenant une
série d'étiquettes label:
suivit de la galaxie associée :
g = galaxy
test1: +f(a) ok.
test2: +f(b) ok.
end
On accède ensuite aux champs d'une galaxie avec ->
:
show g->test1.
show g->test2.
Cela ressemble au concept de record (enregistrement) en programmation sauf que lorsque les galaxies sont utilisées, elles sont représentées par leur constellation sous-jacente (par oubli d'étiquettes).
Substitutions
Les substitutions sont des expressions de la forme [... => ...]
remplaçant
une entité par une autre.
Variables
On peut remplacer des variables par n'importe quel rayon :
print (+f(X))[X=>Y].
print (+f(X))[X=>+a(X)].
Symboles de fonction
On peut remplacer les symboles de fonctions par d'autres symboles de fonction :
print (+f(X))[+f=>+g].
print (+f(X))[+f=>f].
On peut aussi omettre la partie gauche ou droite de =>
pour ajouter
ou retirer un symbole de tête :
print (+f(X); f(X))[=>+a].
print (+f(X); f(X))[=>a].
print (+f(X); f(X))[+f=>].
Tokens et remplacement galactique
Les expressions de galaxies peuvent contenir des variables spéciales
comme #1
, #2
ou encore #variable
.
Ce sont des trous appelés tokens qui peuvent être remplacés par une autre galaxie :
print (#1 #2)[1=>+f(X) X][2=>-f(a)].
Cela permet notamment d'écrire des galaxies paramétriques.
Typage
Le typage en Stellogen illustre des principes majeurs de syntaxe transcendantale où les types sont définis comme des ensembles de tests eux-mêmes définis avec des constellations. La vérification de type implique le passage de tous les tests associés à un type.
Les types sont donnés par une galaxie où chaque champ représent un test à faire passer :
t = galaxy
test1: @-f(X) ok; -g(X).
test2: @-g(X) ok; -f(X).
end
Une galaxie/constellation testée doit donc être confrontée à tous les tests
définis par la galaxie ci-dessous pour être de type t
.
Un type avec un unique test peut être défini par une simple constellation.
Il nous reste cependant à définir ce que signifient :
- être confronté à un test;
- passer un test avec succès.
Conformément à la correspondance de Curry-Howard, les types peuvent être vus comme des formules ou spécifications. On pourrait aussi étendre cela à la proposition de Thomas Seiller de voir les algorithmes comme des spécifications plus sophistiquées.
Checkers
Il faut définir des checkers qui sont des galaxies "juges" définissant :
- un champ
interaction
contenant nécessairement un token#tested
et un autre token#tested
expliquant comment faire interagir un testé et un test; - un champ
expect
indiquant quel est le résultat attendu par l'interaction.
Par exemple :
checker = galaxy
interaction: #tested #test.
expect: { ok }.
end
Typage par test
On peut ensuite faire précéder les définitions par une déclaration de type :
c :: t [checker].
c = +f(0) +g(0).
La constellation c
passe bel et bien les tests test1
et test2
de t
.
Il n'y a donc aucun problème. On dit donc que c
est de type t
ou que c
est une preuve de la formule t
.
Cependant, si nous avions une constellation qui ne passait pas les tests, comme :
c :: t [checker].
c = +f(0).
Nous aurions eu un message d'erreur nous indiquant qu'un test n'est pas passé.
Voici un type avec un unique test pour une représentation naïve des entiers naturels :
nat =
-nat(0) ok;
-nat(s(N)) +nat(N).
0 :: nat [checker].
0 = +nat(0).
1 :: nat [checker].
1 = +nat(s(0)).
On peut aussi omettre de préciser le checker. Dans ce cas, le checker par défaut est :
checker = galaxy
interaction: #tested #test.
expect: { ok }.
end
Cela nous permet d'écrire :
0 :: nat.
0 = +nat(0).
1 :: nat.
1 = +nat(s(0)).
Exercices
Chemins
Donnez une valeur aux variables answer
de sorte à pouvoir exécuter
le code suivant sans erreur de typage.
L'idée est que ces constellations représentent des chemins à compléter de sorte
à obtenir seulement la constellation ok
en résultat.
checker = galaxy
interaction: #test #tested.
expect: { ok }.
end
empty = {}.
answer = #replace_me.
exercise1 :: empty [checker].
exercise1 = ((-1 ok) {1})[1=>answer].
answer = #replace_me.
exercise2 :: empty [checker].
exercise2 = ((-1; +2) {1})[1=>answer].
answer = #replace_me.
exercise3 :: empty [checker].
exercise3 = ((-1 ok; -2 +3) {1})[1=>answer].
answer = #replace_me.
exercise4 :: empty [checker].
exercise4 = ((-f(+g(X)) ok) {1})[1=>answer].
answer = #replace_me.
exercise5 :: empty [checker].
exercise5 = ((+f(a) +f(b); +g(a); @+g(b) ok) {1})[1=>answer].
Solutions
checker = galaxy
interaction: #test #tested.
expect: { ok }.
end
empty = {}.
answer = +1.
exercise1 :: empty [checker]
exercise1 = ((-1 ok) {1})[1=>answer].
answer = +1 -2 ok.
exercise2 :: empty [checker]
exercise2 = ((-1; +2) {1})[1=>answer].
answer = +1 +2; -3.
exercise3 :: empty [checker]
exercise3 = ((-1 ok; -2 +3) {1})[1=>answer].
answer = +f(-g(X)).
exercise4 :: empty [checker]
exercise4 = ((-f(+g(X)) ok) {1})[1=>answer].
answer = -f(a); -f(b) -g(a) -g(b).
exercise5 :: empty [checker]
exercise5 = ((+f(a) +f(b); +g(a); @+g(b) ok) {1})[1 => answer].
Registres dynamiques
Partons du programme suivant :
init = +r0(0).
print process
init.
{replace_me}.
end
représentant une mémoire avec un registre r0
.
Stellogen peut représenter la mémoire mais d'une façon particulière, en
détruisant pour construire ailleurs.
On ne peut pas se contenter de mettre à jour le registre avec une étoile
-r0(X) +r0(1)
car cette étoile a une dépendance circulaire qui lui
permettrait d'être réutilisée un nombre illimité de fois.
En fait, on ne peut que déplacer le registre pour le modifier, par exemple avec
une étoile -r0(0) +r1(1)
qui détruit le registre r0
et construit un
registre r1
contenant 1
.
Le but est de définir des constellations. Vous pouvez utiliser le code ci-dessus pour faire vos tests.
Exercice 1. Définir deux constellations permettant de mettre à jour le
registre r0
à 1
en utilisant une étoile intermédiaire pour sauvegarder la
valeur de r0
.
Solution
-r0(X) +tmp0(X).
-tmp0(X) +r0(1).
Exercice 2. Définir une constellation permettant de dupliquer et déplacer
le registre r0
dans deux registres r1
et r2
.
Solution
-r0(X) +r1(X);
-r0(X) +r2(X).
Exercice 3. Définir deux constellation permettant de mettre la valeur de
r1
à 0
puis définir deux constellations permettant d'échanger les valeurs
de r1
et r2
.
Solution
-r1(X) +tmp0(X).
-tmp0(X) +r1(0).
-r1(X) +s1(X); -r2(X) +s2(X).
-s1(X) +r2(X); -s2(X) +r1(X).
Exercice 4. Comment dupliquer r1
de sorte à pouvoir suivre ses copies et
mettre à jour en une fois (comme si on traitait un seul registre) toutes les
copies à la valeur 5
?
Solution
-r1(X) +r1(l X);
-r1(X) +r1(r X).
-r1(A X) +tmp0(A X).
-tmp0(A X) +r1(A 5).
Exercice 5. En suivant la méthode précédente, dupliquer chaque copie en une seule fois.
Solution
-r1(A X) +r1(l A X);
-r1(A X) +r1(r A X).
Logique booléenne
On veut simuler des formules booléennes par des constellations. Chaque question utilise le résultat de la question précédente.
Exercice 1. Définir une constellation calculant la négation de telle sorte
à ce qu'elle produise 1
en sortie lorsqu'elle est ajoutée à l'étoile
@-not(0 X) X
et 0
lorsqu'ajoutée à @-not(1 X) X
.
Solution
not = +not(0 1); +not(1 0).
Exercice 2. Comment afficher la table de vérité de la négation avec une
seule étoile, de sorte à ce qu'on obtienne en sortie
table_not(0 1); table_not(1 0).
?
Solution
print @-not(X Y) table_not(X Y).
Exercice 3. Ecrire de deux manières différentes des constellations calculant la conjonction et la disjonction et afficher leur table de vérité de la même façon que pour la question précédente.
Solution
and = +and(0 0 0); +and(0 1 0); +and(1 0 0); +and(1 1 1).
or = +or(0 0 0); +or(0 1 1); +or(1 0 1); +or(1 1 1).
and2 = +and2(0 X 0); +and2(1 X X).
or2 = +or2(0 X X); +or2(1 X 1).
print @-and(X Y R) table_and(X Y R).
print @-or(X Y R) table_or(X Y R).
print @-and2(X Y R) table_and2(X Y R).
print @-or2(X Y R) table_or2(X Y R).
Exercice 4. Utiliser la disjonction et la négation pour afficher la table
de vérité de l'implication sachant que X => Y = not(X) \/ Y
.
Solution
impl = -not(X Y) -or(Y Z R) +impl(X Z R).
impl2 = -not(X Y) -or2(Y Z R) +impl2(X Z R).
print @-impl(X Y R) table_impl(X Y R).
print @-impl2(X Y R) table_impl2(X Y R).
Exercice 5. Utiliser l'implication et la conjonction pour afficher la table
de vérité de l'équivalence logique sachant que X <=> Y = (X => Y) /\ (X => Y)
.
Solution
eqq = -impl(X Y R1) -impl(Y X R2) -and(R1 R2 R) +eqq(X Y R).
eqq2 = -impl2(X Y R1) -impl2(Y X R2) -and2(R1 R2 R) +eqq2(X Y R).
table_eqq = @-eqq(X Y R) table_eqq(X Y R).
table_eqq2 = @-eqq2(X Y R) table_eqq2(X Y R).
Exercice 6. Définir une constellation représentant la formule du tiers
X \/ ~X
. Afficher la table de vérité correspondant à cette formule.
Solution
ex = -not(X R1) -or(R1 X R2) +ex(X R2).
print -ex(X R) table_ex(X R).
Exercice 7. Déterminer pour quelles valeurs de X
, Y
et Z
la formule
X /\ ~(Y \/ Z)
est vraie.
Solution
print -or(Y Z R1) -not(R1 R2) -and(X R2 1) x(X) y(Y) z(Z).
Introduction
There are multiple ways to encode objects:
- Algebraic encodings, which assign meaning to function symbols. Objects are encoded as rays;
- interactive encodings, which align more closely with the philosophy of transcendental syntax. Objects are encoded as constellations. Specifically, the dynamics of objects are encoded in the dependencies between rays, and their meaning is provided by their types.
Algebraic Encoding
Prolog uses algebraic encodings. For example, a pair could be represented by a
ray +pair(X Y)
. The components are other terms. Functions acting on pairs are
stars containing a ray like -pair(X Y)
or using function symbols to represent
predicates/properties, such as +exchange(+pair(X Y) +pair(Y X))
.
Interactive Encoding
In an interactive encoding (closer to proof theory, particularly to the theory of proof nets in linear logic), each component is represented by a constellation.
There are two types of pairs:
- Tensor product, where we have a union of two disjoint constellations. Projections may not be defined in this case.
- Cartesian product, where something distinguishes each component, and we have the ability to extract a specific component (left or right).
The difference between these two types of encoding is still under exploration. I welcome feedback if anyone has insights. I believe this distinction has implications for expressiveness and ease of type manipulation.
Special Symbols
Stellogen defines a binary infix symbol :
that is right-associative, allowing
us to write a:X
instead of :(a X)
or cons(a X)
.
This enables readable concatenation of symbols, for example:
+f(a:b:c:e).
Sequences
Algebraic case
Fixed sequences
+seq(a b c d).
We can retrieve the head element with:
head = +head(-seq(X1 X2 X3 X4) X1).
query = -head(+seq(1 2 3 4) R) R.
print (query head).
Constants stack
+list(a(b(c(e)))).
We can push or pop a head element with:
l = +list(a(b(c(e)))).
print process
l.
'push
-list(X) +tmp(new(X)).
-tmp(X) +list(X).
'pop
-list(new(X)) +list(X).
end
Remark. We cannot reason on an generic function symbol. Therefore, it is only possible to push and pop specific symbols. To fix this, we must use function symbols as constructors instead (and not as a value).
General lists
We can imagine several equivalent representations by using function symbols to concatenate elements together.
+list(a:b:c:d:e).
+list(cons(a, cons(b, cons(c, cons(d, e))))).
+cons(a, +cons(b, +cons(c, +cons(d, e)))).
It is possible to add and remove elements like this:
l = +list(a:b:c:d:e).
print process
l.
'push
-list(X) +tmp(new:X).
-tmp(X) +list(X).
'pop
-list(C:X) +list(X).
end
By following the principles of logic programming, it is possible to check if a list is empty:
empty? = +empty?(e).
print empty? @-empty?(e) ok.
print empty? @-empty?(1:e) ok.
Concatenate two lists:
append =
+append(e L L);
-append(T L R) +append(H:T L H:R).
print append @-append(a:b:e c:d:e R) R.
Reverse a list:
rev =
+revacc(e ACC ACC);
-revacc(T H:ACC R) +revacc(H:T ACC R);
-revacc(L e R) +rev(L R).
print rev @-rev(a:b:c:d:e R) R.
Apply a function over all elements of a list:
map =
+map(X e e);
-funcall(F H FH) -map(F T R) +map(F H:T FH:R).
print
map
+funcall(f X f(X));
@-map(f a:b:c:d:e R) R.
Interactive case
Sets
c = +e1(a); +e2(b); e3(c).
Chains
c =
@-e(1 X) +e(2 a);
-e(2 X) +e(3 b);
-e(3 X) +e(4 c);
-e(4 X) +e(5 d).
Linked lists
c =
@+e(1 e);
-e(1 X) +e(2 a:X);
-e(2 X) +e(3 b:X);
-e(3 X) +e(4 c:X);
-e(4 X) +e(5 d:X).
Hypergraphes
Arbres
(TODO)
Graphes
(TODO)
Entiers
Entiers naturels
(TODO)
Entiers relatifs
(TODO)
Entiers binaires
(TODO)
Entiers en base 10
(TODO)