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é :

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 ou Var);
  • des fonctions de la forme f(t1, ..., tn)f est un symbole de fonction (commençant par une minuscule ou un chiffre) et les expressions t1, ..., 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 pas X comme sous-terme, sinon nous aurions une dépendance circulaire, comme entre X et f(X);

  • une fonction f(t1, ..., tn) est compatible avec f(u1, ..., un)ti est compatible avec ui pour tout i.

  • f(X) et f(h(a)) sont compatibles avec {X:=h(a)};

  • f(X) et X sont incompatibles;

  • f(X) et g(X) sont incompatibles;

  • f(h(X)) et f(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 lorsque f(t1, ..., tn) est confronté à g(u1, ..., un), on demande que f et g 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) et f(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')

theta est la substitution la plus générale induite par r et r'.

Remarquez que:

  • r et r' sont annihilées pendant la fusion;
  • les deux étoiles fusionnent;
  • la substitution obtenue par résolution du conflit entre r et r' 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 voisin X. Le résultat est donc X{X:=a} soit a.

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 :

  1. selectionner un rayon r d'une étoile d'état s;
  2. chercher toutes les connexions possibles avec des rayons r' d'étoiles d'action a;
  3. dupliquer s à droite pour chaque tel rayon r' trouvé car il y a plusieurs interactions possibles à satisfaire;
  4. remplacer chaque copie de s par la fusion entre a et s;
  5. 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).
  1. @+f(X); -f(a).
  2. @+f(X); -f(Y) a.
  3. @+f(X) X; -f(a); -f(b).
  4. @+1(X) -2(X); -2(X) +3(X).
  5. @-1(X) +2(X); -2(X) +1(X).
  6. @-1(X) +2(X); -2(X) +1(X).
  7. @-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)

Automate finis

Transducteurs

Automates à pile

Machines de Turing (TODO)

Lambda-calcul linéaire non typé (TODO)

Types linéaires (TODO)

Lambda-calcul simplement typé (TODO)