Un langage de programmation polymorphe et contextuel

1) Introduction

Le langage de programmation que nous mettons en oeuvre est polymorphe et contextuel, il change d'un contexte à un autre. Autrement dit, le contexte contient une partie substantielle de la définition du langage qui est donc changeable en cours de route. Et ses objets possèdes une multitude de types possibles déterminés par un mécanisme d'inférence.

Ce langage s'obtient à partir d'une interprétation des structures mathématiques dans un formalisme qui va préciser lui-même sa syntaxe.

L'interpréteur est programmé ici intepreteur

2) Langage d'opérateurs typés

On se place dans un cadre général et formel. On pose par exemples des types `A,B,C` qui sont comme des ensembles munis d'opérateurs jouant le rôle de méthode.

Le type Elément noté `Omega` est le type le plus général. Il englobe tous les éléments. Les applications sont des éléments. Les couples d'éléments sont des éléments. Les éléments sont aussi appelée des opérateurs.

Le type `A"→"B` est le type des opérateurs qui transforment un opérateur de type `A` en un opérateur de type `B`.

Le type `A"×"B` est le type des couples d'opérateurs comprenant un premier opérateur de type `A` et un second opérateur de type `B`.

Les 2 symboles `"×", "→"` désignent des opérateurs de types. Ce sont des opérateurs binaires qui prennent comme argument, deux types, et produisent un type. Par convention l'opérateur "produit directe" noté `"×"` possède une priorité syntaxique plus élevée que celle de l'opérateur "transformation" noté `"→"`. Ainsi l'expression `A"×"A"→"B` signifie `(A"×"A)"→"B` et non `A"×"(A"→"B)`.

3) Le typage par inférence

On pose une liste d'opérateurs dont on précise le type. Par exemple :

Opérateur
Type
`a`
`A`
`f`
`A → B`
`f`
`A×A → B`
`f`
`B×B → A`
`g`
`B → A`
`"+"`
`A×A → A`
`"∗"`
`A×A → A`

Deux opérateurs distincts peuvent posséder le même nom. Et c'est la détermination de leur type qui pourra permettre de les différencier. Notez que ce sont les typages déclarés des opérateurs qui présidera à cette détermination et non leurs typages réels. L'opérateur est déterminé par son prototype qui est une expression plus complète que le seul nom, regroupant le nom de l'opérateur et son type, tel que par exemple :

`f:A"×"A"→"B`

Le symbole deux points est un meta-opérateur binaire qui prend comme premier argument un nom de varibable et comme second argument un type. Il permet de préciser l'identification d'un objet par son nom et son type. Une autre représentation est aussi utilisée, consistant à mettre le type en exposant ou en indice. Ainsi `e^A` et `e^B` désigne l'élément `e` de type `A` et l'élément `e` de type `B`, sachant qu'ils peuvent être égaux ou distincts. Le type constitue une structure mathématique, et le symbole `e` est un nom qui désigne dans chaque structure de langage contenant le nom `e`, un élément de la structure.

Une expression tel que `f(x,y)` entraine une contrainte sur les types possibles des différents opérateurs en présence. La résolution de cette contrainte s'appelle l'inférence de type. Dans l'exemple elle produit 3 solutions, où le typage est précisé en exposant :

`f^(A → B)(x,y)^A`
`f^(A×A → B)(x^A,y^A)`
`f^(B×B→ A)(x^B,y^B)`

Remarquez que l'on donne un type à un couple, car un couple constitue un élément. En effet, si `x` est de type `A` et si `y` est de type `B`, alors `(x,y)` est un élément de type `A"×"B`. Tout opérateur peut être perçu comme élément et réciproquement, faisant que l'élément est synonyme d'opérateur.

4) La syntaxes des opérateurs

Pour donner toute la liberté au langage, puisque tout est opérateur, il convient de définir précisement ce que peut être la syntaxe d'un opérateur.

4.1) L'universalité de l'élément

Le principe d'un élément est d'être un identifiant, autrement-dit de pouvoir s'identifer. Tout élément peut être rendu égal à un autre élément quel qu'il soit. S'ils sont initalement distincts, on les rend égaux en fusionnant leurs rôles en les regroupant sur un seul élément. Les rôles sont alors simplement ajoutés sur un même identifiant. Et, si l'élément détermine son identité, c'est la syntaxe (une syntaxe non-ambigüe) qui détermine quel rôle l'élément met en oeuvre.

L'élément est un opérateur. S'il ne prend aucun argument, c'est un opérateur nullaire. S'il prend un argument, c'est un opérateur unaire. S'il prend deux arguments, c'est un opérateur binaires. Etc., et il peut cummuler les rôles. Et c'est la syntaxe (non-ambigüe) qui détermine qu'elle rôle est joué.

Du fait de ces égalités entre éléments qu'il est possible de poser arbitrairement, chaque type devient susceptible de pouvoir se décomposer. Cette relativitée des types fait que l'opérateur n'a pas d'arité bien définie. L'arité est alors entièrement définie dans la syntaxe de l'opérateur. Un opérateur binaire peut être perçu comme un opérateur unaire et aussi comme un opérateur nullaire. Et c'est la syntaxe qui précise quel rôle est mis en oeuvre.

Une syntaxe est dite non-ambigue si elle n'engendre pas plusieurs interprétation possible. Le principe est qu'il doit être possible d'overloader des fonctions de même nom mais ayant une syntaxe non-ambigue distincte. On fait le choix d'inclure la syntaxe non-ambigue dans le type. Ce qui permet de poser comme principe sans réduire notre capacité expressive que la fonction est totalement déterminée uniquement par son nom et son type, Chose qui doit être, car un opérateur est un élement et qu'un élément est ainsi déterminé.

Ainsi chaque application est identifiée par un nom, et un type. Le même nom d'opérateur peut être utilisé avec différentes types pour définir différentes fonctions. Cette faculté provient de la notation des physiciens qui identifie le résultat par le nom de la fonction mais qui utilise un typage ou une syntaxe différente si celle-ci utilise des arguments dans un autre système de coordonnées.

4.2) Opérateur nullaire

Un opérateur nullaire n'a qu'une seule syntaxe, il se note par son nom qui est une chaine de caractères. Mais l'opérateur n'est pas complétement identifié par son nom. Il l'est par son prototype qui précise son type. Par exemple :

`x:A`

Cette expression désigne l'opérateur nullaire de nom `x` et de type `A`. Le type se comporte comme une structure mathématique possédant un langage contenant le nom `x`, et ce nom `x` désigne un élément précis dans la structure `A`.

`x:A` représente l'élément `x` de `A` tandis que `x:B` représente l'élément `x` de `B`. Les ensembles sous-jacent des types `A` et `B` ne sont pas nécessairement disjoints, et `x:A` et `x:B` peuvent être égaux ou distincts.

Remarquez que `A` et `B` peuvent être égaux en tant qu'ensemble sous-jacent et aussi en tant que langage, sans que `x:A` ne soit égal à `x:B`. Car le langage de `A`, même si c'est un même langage que celui de `B`, ne désigne pas forcement la même chose que le langage de `B`.

En résumé, le type `A`, tel une structure, possède un langage qui constitue une structure libre notée `ccL_A`, et possède un ensemble sous-jacent `A`. Et il possède un morphisme `sigma_A` du langage `ccL_A` vers l'ensemble sous-jacent `A` qui détermine sa signification (Voir chapitre 8, le langage d'un type).

4.3) Opérateur unaire

L'opérateur unaire peut facilement regrouper tous les opérateurs non-nullaires, car il peut regrouper le `n`-uplet d'entrée en un seul élément.

Un opérateur unaire `f` possède un type de la forme `A"→"B`. Il désigne une application. Et, comme nous faisons le choix d'accorder la plus grande liberté au langage, chaque type peut définir un opérateur `f` différent. C'est le typage qui déterminera quelle est la fonction `f` évoquée.

Un opérateur unaire `f` en l'appliquant à `x` possède une forme syntaxique non-ambigüe parmi `f_x`, `x^f`, ou avec parenthèses `f(x)`, `f[x]`, `f{x}`, `...`

Un paramètre global à notre système définira la catégorie des symboles de parenthèses, où chaques parenthèses est soit ouvrantes ou fermante, et où à chaque parenthèse ouvrante correspond une parenthèse fermante : `(),[],"<>",{}, ...`. Notez que ces parenthèses délimitent de faite des blocs susceptibles de posséder un contexte spécifique définissant un langage local. Chaque tel opérateur unaire définit un bloc contenant ses arguments, appelé bloc d'entête, à ne pas confondre avec le bloc d'appel qui est le bloc contenant l'appel de l'opéraeur. Les formes syntaxiques avec indice ou avec exposant sont de même nature que celles avec parenthèses et possède aussi un bloc d'entête.

Les priorités syntaxiques de la plus haute à la plus faible sont : l'indice, l'exposant, la parenthèse. Ainsi, considérons l'expression suivante :

`f_a^b(c)`

Ce terme applique `f` à `a`, puis le résultat est appliqué à `b`, puis le résultat est appliqué à `c`. L'appel en passant les arguments par indice ou par exposant est de même nature que l'appel en passant les arguments entre parenthèses, il utilise un bloc d'entête. De ce fait, lors d'une composition successive d'un même opérateur, l'ordre d'évaluation est nécessairement défini en profondeur d'abord. Par exemple le terme `f(f(f(a)))` ne peut s'évaluer que d'une seul façon.

Comme nous faisons le choix d'accorder la plus grande liberté au langage, chaque forme syntaxique non-ambigüe peut définir un opérateur `f` différent, et donc constitue un type différent. Et c'est bien le type qui déterminera quelle est l'application `f` évoquée. Le type comprend la syntaxe non-ambigüe. Le type et la syntaxe non-ambigüe forme ainsi un type plus précis. La syntaxe non-ambigüe est codifiée par l'un des sigles : `"i"`, `"e"`, `()`, `[]`, `"<>"`, `{}`, `...` que l'on associe à l'opérateur de type de transformation `"→"` en le plaçant juste dessus. Et la syntaxe par défaut est `()`. Par exemple :

`f":"Aoverset("[]")("→")B`

Cette expression désigne une fonction de nom `f`, qui transforme un élément de type `A` en un élément de type `B`, et de syntaxe appliquée à `x` qui donne `f[x]`. Si la syntaxe n'est pas mentionnée, la syntaxe par défaut est `()`. Ainsi, le type `A"→"B` représente toutes les applications qui transforment un élément a de type `A` en un élément de type `B` exprimé par.

Le nom peut être vide, auquel cas seules les syntaxes avec parenthèses qui ne sont ni indicielles ni exponentielles peuvent être utilisées. Ainsi l'expression `[]:A"→"B` représente une application qui note l'image de `x` par `[x]`.

Chaque forme de parenthèse, si elle ne constitue pas un opérateur, joue le rôle de priorisation. Et si elle constitue un opérateur, elle continue encore de prioriser par nécessité mais procède par la suite à l'appel de l'opérateur correspondant.

Les parenthèses délimitent un bloc. Donc elles peuvent contenir un contexte. Donc elle peuvent contenir une expression dans un langage spécifique défini par ce contexte. On parlera du contexte d'entête.

On ajoutera une dernière forme de syntaxe dite collée, qu'il faut paramètrer afin qu'elle ne soit pas ambigüe. Elle sera étudier en même temps que l'opérateur de juxtaposition et l'opérateur binaire.

4.4) Forme nullaire d'un opérateur unaire

L'opérateur unaire étant un élément, on doit pouvoir le désigner sans l'appliquer à quoi que ce soit. Mais comme il peut y avoir plusieur opérateurs unaires de mếme nom et de type distinct mais uniquement par une syntaxe non-ambigüe différente, il faut pouvoir les nommer distinctement. Cela se fait en précisant sa syntaxe non-ambigüe comme une partie de son type. L'opérateur unaire de syntaxe `"i"`, `"e"`, `()`, `[]`, `"<>"`, `{}`, `...` est respectivement identique à l'opérateur nullaire de nom `f":i"`, `f":e"`, `f":()"`, `f":[]"`, `f":<>"`, `f":{}"`, ...`.

Puis on peut envisager une écriture plus condencée :

`overset("ø")f, overset("i")f, overset("e")f, overset("()")f, overset("[]")f,overset("<>")f,overset("{}")f`

5) Opérateur de type

Nous avons prédéfini un opérateur de type `"→"` qui est binaire et de syntaxe centrée. Nous avons prédéfini un second opérateur de type `"×"` qui est binaire, de syntaxe centrée et de prioritée syntaxique plus élevée.

5.1) Associativité de `"×"`

L'opérateur `"×"` est associatif. Etant donné trois types `A,B,C`, l'expression `A"×"B` désigne le type produit directe de conception associative. C'est l'ensemble des couples d'éléments comprenant un premier élément de type `A` et un second élément de type `B`, mais conçus de façon associative. C'est à dire que le type `(A"×"B)"×"C` est identique au type `A"×"(B"×"C)` que l'on note sans parenthèse de façon unique par `A"×"B"×"C`.

5.2) Currification

Le type `A"→"(B"→"C)` est équivalent au type `A"×"B"→"C`. Cela signifie que l'on passe de l'un à l'autre à volonté, et donc que les méthodes de ces deux types sont regroupées pour former qu'un seul type. Il existe donc plusieurs expression pour désigner le même type. Et il y a une forme normale choisie qui consiste à privilègier l'apparition du produit `"×"` par rapport à l'opérateur `"→"`, faisant que le second membre de l'opérateur `"→"` est nécessairement un produit ou un type de base (non-décomposable).

5.3) Distributivité de `"→"` sur `"×"`

Le type `A"→"(B"×"C)` est équivalent au type `(A"→"B)"×"(A"→"C)`. Cela signifie que l'on passe de l'un à l'autre à volonté, et donc que les méthodes de ces deux types sont regroupées pour former qu'un seul type. Et il y a une forme normale choisie qui consiste à privilègier l'apparition au premier niveau, du produit `"×"` par rapport à l'opérateur `"→"`, décomposant ainsi à chaque fois les seconds membres qui sont des produits, faisant que, en appliquant également la currification, le second membre de l'opérateur `"→"` est nécessairement un type de base (non-décomposable).

5.4) Méta-opérateur

Nous avons prédéfini un méta-opérateur binaire, le deux point `:` de syntaxe centrée. Etant donné un nom d'objet `x` et un type `A`. Le nom d'objet n'est pas suffisant pour désigner un objet précis sauf dans les cas où il n'y a pas d'alternative ou que celui-ci possède un type par défaut. L'objet est précisé en désignant son type déclaré qui comprend sa syntaxe non-ambigue utilisée.

--- 2 juillet 2022 ---

 

 

 

L'expression `x":"A` désigne n'importe quelle objet de nom `x`, de type `A`, et de syntaxe quelconque.

L'expression `x"|d"` désigne un objet de nom `x` de syntaxe opérateur unaire droit c'est à dire qui appliqué à `e` produit `ex` et de tous les types possibles évoqués dans le contexte.

L'expression `x"|d:"A` ou `x":"A"|d"` désigne un objet de nom `x` de syntaxe opérateur unaire droit, et de type `A`.

6) L'opérateur de juxtaposition

Néanmoins une difficulté apparait du fait de l'usage de l'opérateur de juxtpoition qui correspond à un protocole de bas niveau dans le lanage, et qui doit pourtant s'intégrer dans notre dispositif comme n'importe quel opérateur. Pour cela, on reprend le problème à la base. La formule est une chaine de caractères Unicode. L'interpréteur reconnait dans cette chaine les noms d'opérateurs qu'il a dans son dictionnaire en donnant une priorité aux noms les plus longs. Et il reconnait les niveaux de parenthèses.

Puis, comme on ne veut pas d'un langage verbeux nécessitant de tout prédéclarer, il se peut que de nouveaux noms d'opérateur apparaissent. Ces nouveaux opérateurs ne peuvent pas être séparés entre eux par simple juxtaposition puisqu'ils ne sont pas connus du dictionnaire. Dans ce cas on utilise le caractère blanc ou une succession de caractères blancs comme séparateur pour permettre de juxtaposer plusieurs éléments inconnus, ou on les entoures de parenthèses. Mais la juxtaposition d'éléments ne nécessite pas toujours de faire cela. L'interpréteur connaissant `x` et `y` mais ne connaissant pas `xy` interprétera la formule `xy` comme la juxtaposition de `x` et de `y`. Pour ajouter le mot `xy` dans le dictionnaire, on entourera de quottes le premier usage de ce mot `"'"xy"'"`. Celui-ci sera ainsi introduit dans le dictionnaire. Parcontre, si l'interpréteur ne connait pas ni `x` ni `y` ni `xy` alors il interprétera la formule `xy` comme un seul nouvel opérateur et l'ajoutera au dictionnaire. Et il interprétera la formule `x y` où le séparateur est un ou plusieur caractères blancs, comme la juxtaposition de `x` et de `y`.

On définit ainsi la syntaxe de l'opérateur de juxtaposition, qui est soit composée d'un ou de plusieurs caractères blancs, ou qui est soit l'interstice entre un objet et une parenthèse ouvrante ou entre une parenthèse fermante et un objet, ou qui est une juxtaposition reconnue par le dictionnaire, et ne devant pas s'inscrir pas dans un appel de fonction. La juxtaposition est un opérateur binaire qui regroupe les deux formes de syntaxe, la séparation par des caractères blancs ou des parenthèses, et la juxtaposition reconnue par le dictionnaire. Ces formes sont toutes deux, de syntaxe centrée, soit avec comme symbole un ou plusieurs caractères blancs, ou soit avec absence de symbole.

La juxtaposition faisant partie d'un protocole de bas niveau dans le langage, le choix qui est fait consiste à traiter les données juxtaposées sans prioriser aucune des parties. Autrement dit, l'opérateur de juxtaposition est associatif. Plus exactement, on s'interdit de définir un opérateur de juxtaposition qui ne soit pas associatif.

L'opérateur de juxtaposition est associatif, et c'est le contexte qui dira quelle est cette opération associative. Son nom sous forme nullaire est `"juxtaposition"`.

4.5) Opérateur unaire collé gauche ou droite

On ajoute une syntaxe supplémentaire

 

`fx`, `xf`,

Les syntaxes `fx` et `xf` sont traités différement car elle n'utilise pas de bloc d'entête contenant leurs arguments. Ces deux syntaxes ne se différencient pas à la lecture, elles sont ambigüe. Puis, lors de leur évaluation

--------

 

Par contre, lorsque la syntaxe n'utilise pas de paranthèse (ni le passage à l'indice ou à l'exposant), l'ordre d'évaluation doit être préciser est déterminé par les priorités syntaxique des opérateurs unaires, et par leur sens d'évaluation.

 

Ces dernières ne sont pas des syntaxes indentifiables, c'est à dire que, en analysant le terme `ff` on ne peut savoir si le rôle de `f` est de s'appliquer sur l'élément de gauche ou de droite. C'est pourquoi on ne peut pas overloader ces fonctions si l'une existe déjà, et donc ces différents comportements ne constituent qu'un seul type, qui par contre possèdera un attribut contextuel précisant la syntaxe droite, notée `"d"`, ou la syntaxe gauche, notée `"g"`, et qui précisera également un sens d'évaluation ; en profondeur d'abord, noté `"p"`, ou en tête d'abord, noté `"t"`. Ainsi on note la forme de syntaxe par le symbole `"ø"` qui représente les 4 syntaxes possibles `"gp", "gt", "dp", "dt"`.

 

 

 

Puis le type `Aoverset("ø")("→")B` possède un attribut appartenant à `{"gp", "gt", "dp", "dt"}` qui est contextuel

L'opérateur de type syntaxique "ø" possède un sens gauche et un sens d'évaluation par défaut en profonfeur d'abord, c'est à dire :

`f":"overset("ø")(Omega"→"Omega)`
`ffff "==" f(f(f(f)))`

Remarquez que les parenthèses ici ne sont pas des parenthèses d'appel mais de simple parenthèse de priorisation. On spécifit un sens d'évaluation en tête d'abord en modifiant l'attribut.

`f":"overset("ø")(Omega"→"Omega)`
`f."sens" = "t"`
`ffff "==" f(f)(f)(f)`

 

 

 

Des fonctions unaires de syntaxe droite, `d`, et gauche, `g`, peuvent se cotoyer. Il faut alors gérer le conflit naissant, tel que par exemple dans l'expression `fxg` avec `f` unaire de syntaxe gauche et `g` unaire de syntaxe droite.

Une priorité syntaxique est définie. Par défaut, la dernière définition (qui peut être limité ou non à un type et à une syntaxe) est prioritaire sur les précédentes.

4.5) Opérateur binaire

On se limite pour l'opérateur binaire à une seule syntaxe dite collé-centrée, mais on verra plus tard que l'on peut définir, comme pour l'opérateur unaire, une syntaxe collé-gauche et une syntaxe collé-droite s'inscrivant dans une logique polonaise où les arguments sont juxtaposés. L'opérateur binaire `"⦁"` appliqué à `x` et `y` se note `x"⦁"y`. Et il n'y a pas de version avec parenthèses car cela est faisable et suffisant à travers la forme unaire.

Donc l'opérateur binaire n'a pas de bloc d'entête. Mais il apparaît une ambiguité qui nécessite pour être levée de fixer un sens d'évaluation. Par exemple, considérons l'expression suivante :

`a+b+c+d`

L'ordre d'évaluation de droite à gauche correspond à `a+(b+(c+d))`. On commence par évaluer l'argument de droite puis l'argument de gauche, puis l'opérateur. Donc on d est d'abord évalué puis c, puis c+d, puis b, puis b+(c+d), puis a, puis `a+(b+(c+d))`. Tandis que le sens d'évaluation de gauche à droite correspond à `((a+b)+c)+d`. On commence par évaluer l'argument de gauche puis l'argument de droite, puis l'opérateur. Donc `a` est d'abord évalué, puis `b`, puis `(a+b)`, puis c, puis (a+b)+c, puis d , puis `((a+b)+c)+d`

Dans les cas où l'opérateur peut faire partie des arguments, une autre ambiguité apparaît. `(a++b)` peut être interprété comme a+

 

 

 

L'ordre d'évaluation en profondeur d'abord correspond à `a+(++b)`, tandis que l'ordre d'évaluation en entête d'abord ((a+b)+c)+d.

Le nom peut être vide, désignant l'opérateur de juxtaposition (voir chapitre n°6).

Une priorité syntaxique est définie. Par défaut, la dernière définition typée est prioritaire sur les précédentes.

La syntaxe centrée d'un opérateur binaire `"⦁"` appliqué à une liste d'au moins 2 éléments `a,b,c,d` se note `a"⦁"b"⦁"c"⦁"d`. Et si l'opérateur est associatif, il n'y a pas besoin de spécifier un sens d'évaluation. Dans le cas non associatif, il y a un sens d'évaluation par défaut en profondeur d'abord faisant que :

`a"⦁"b"⦁"c"⦁"d``=``a"⦁"(b"⦁"(c"⦁"d)))`

On spécifit un sens d'évaluation en tête d'abord comme suit.

`"⦁:"Omega"×"Omega"→"Omega`
`"⦁"."sens"="t"`
`a"⦁"b"⦁"c"⦁"d "==" ((a"⦁"b)"⦁"c)"⦁"d`

 

7) La définition de la fonction identitée

Considérons l'expression suivante :

`f = x|->x`

Le signe égale `"="` qui est un opérateur binaire représentant ici une affectation, est de priorité plus faible que celle de l'opérateur de définition de fonction `|->`. L'opérateur de comparaison est le double-égale `"=="`. L'opérateur `|->` ne s'applique directement sur des éléments mais sur des termes avec variables, désignants des éléments fonction de ces variables. À sa gauche, il voit le terme constitué d'une seule variable `x` de type `Omega`, et à sa droite il voit la même chose. C'est la définition de la fonction identité dans `Omega`. Comme aucune syntaxe n'est précisée, c'est la syntaxe par défaut qui a cours. Ainsi, il découle de cette instruction ce qui suit :

`f:Omega"→"Omega`
`f(f) "==" f`

Remarquez la forme nullaire `f` et la forme unaire `f(".")` qui désigne le même opérateur mais dans deux rôles différents, l'un passif, l'autre actif.

8) Le langage d'un type

Etant donné un type `A`, celui-ci possède un langage `ccL_A`, un ensemble sous-jacent `A` et un morphisme `sigma_A` du langage `ccL_A` vers l'ensemble sous-jacent `A`, qui determine ce que désigne le langage `ccL_A`.

Le langage de `A` est un ensemble d'opérateurs générateurs, et constitue une structure libre. C'est à dire que toutes les compositions finies de ces opérateurs générateurs constituent des termes distincts de ce langage. `sigma_A` est la fonction de désignation. Appliqué à un terme du langage, elle retourne l'élément désigné par le terme. Autrement dit, `sigma_A` détermine la signification du langage `ccL_A`.

On remarque que `sigma_A` doit ếgalement transporter les rôles des éléments. Ainsi par exemple, considérons les opérateurs suivants où leur type sont notée en exposant :

`a^A,b^A,f^(A->A),"g"^(A×A->A) ,"+"^(A×A->A)`

Notez qu'après cette déclaration, les éléments `a,b,f,+` auront ces types respectifs par défaut. De même, il doit être possible de spécifier par défaut à quel structure correspond le langage `ccL=ccL_A` et le morphisme `sigma=sigma_A`.

Pour désigner les mots du langage `ccL`, on les entoure de guillemets. Ainsi `"‹"a"›","‹"f"›","‹"g"›","‹+›"`, désignent les opérateurs générateurs de `ccL`. Et les termes du langage `ccL` entourés de ces guillemets se décompose en une composition d'opérateurs comme suit :

`"‹"g(a,f(a))"›" = "‹"g"›"("‹"a"›","‹"f"›"("‹"a"›"))`

Tout se passe comme si un contexte particulier est associé aux blocs délimité par ces crochets `"‹ ›"

---- 2 juin 2022 ----

 


`"‹"abc`
`»abc`
`"›"abc`
`„abc`
`“abc`
`‟abc`
`”abc`
`’abc`
`"abc`
`❝abc`
`❞abc`
`❮abc`
`❯abc`
`⹂abc`
`〝abc`
`〞abc`
`〟abc`
`"abc`
`‚abc`
`‘abc`
`‛abc`
`❛abc`
`❜abc`
`❟abc`

 

 

La signification du langage, va respecter ces différents rôles en les transportant sur l'ensemble sous-jacent. Autrement-dit, nous avons : `AA x in ccL_A, AA y in ccL_A,`

`sigma('a')=a`
`sigma('b')=b`
`sigma('f')=f`
`sigma('f('x')')= sigma('f')(sigma(x)) =f(sigma(x))`
`sigma('g('x','y')') = sigma('g')(sigma(x),sigma(y)) =g(sigma(x),sigma(y))`
`sigma(x'"+"'y)= sigma(x) sigma('"+"') sigma(y) = sigma(x) "+"sigma(y)`


 

`sigma_A` est un morphisme, car chaque opérateur générateur du langage

9) Les types Language

Un terme est une composition d'opérateurs. On note `Omega[x]`, les termes contenant une variable `x`. Cela signifit que l'on utilise le nom `x` pour désigner cette nouvelle variable, et que l'on procède de façon analogue à une extension élémentaire. Et s'il existe déjà un élément de nom `x`, celui-ci est alors masqué.

--- 27 juin 2022 ---

Etant donné un type `A` celui-ci comprend un ensemble sous-jacent et un langage. Et dans ce langage, chaque terme désigne un élément de l'ensemble sous-jacent `A`. On note `A[x]` l'ensemble des termes du langage `A` avec une variable `x`.

La fonction

 

8) Morphisme

Les types `A,B` sont des structures possèdant des langages spécifiques qui peuvent coïncider sans pour autant désigner la même chose. Le dialogue entre type est assurer par les morphismes. Un morphisme `varphi` de `A "→" B` est une application qui respecte un certains nombres d'opérateurs de noms qui peuvent être communs aux deux structures. C'est à dire par exemple pour le respect de l'élément `e`, de l'opérateur unaire `f` et de l'opérateur binaire `+` :

`varphi(e_A) "==" e_B`

`AAx"∈"A, varphi(f_A(x_A)) "==" f_B(varphi(x_A))`

`AAx"∈"A, AAy"∈"A, varphi(x_A "+"_A y_A) "==" varphi(x_A) "+"_B varphi(y_A)`

Le type des éléments est noté ici en indice. Le morphisme `varphi` transporte le langage `(e,f,+)` de la structure `A` respectivement sur le langage `(e,f,+)` de la structure `B`. Et on peut définir des morphismes qui transportent des langages différents mais de même signature c'est à dire transportant chaque opérateur sur un opérateur de même arité. Le morphisme exemple se note comme suit :

`varphi = (A"→"B,{e"↦"e, f"↦"f, "+↦+"})`

 

---- 26 juin 2022 ----

 

 

 

 

 

Les opérations sur les types correspondent à des opérations sur les éléments.

L'opérateur "transformation" `->` va définir au niveau des éléments l'opérateur de "transformation" `|->` et l'opérateur d'application `"@"`

`|-> : Omega×Omega->(Omega->Omega)`

Ainsi l'opérateur "produit directe", `"×"` qui se situe au niveau des types, va définir au niveau des éléments, l'opérateur virgule et les opérateurs de "projection" `g,d`.

`,:Omega×Omega->Omega`
`(x,y) : Omega`

`g:Omega×Omega`
`g(x,y) = (x,y)|->x`

`d:Omega×Omega`
`g(x,y) = (x,y)|->y`

---- 25 juin 2022 ----

 

 

-------

On ajoute à cela une forme supplémentaire qui est nullaire, et pour appliquer `f` à `x`, on utilise un autre opérateur, un opérateur binaire d'application noté `"@"`, prenant en argument une application et un élément, et produisant l'image de l'élément transformé par l'application. On prédéfinie cet opérateur binaire d'application, noté `"@"`, de syntaxe centrée avec une priorité syntaxique la plus haute. Ainsi nous avons ;

`f"@"x`

---

`f":"A"→"A`
`a":"A`
`(f,a)":"(A"→"A)"×"A`

La première instruction déclare une fonction `f` de `A"→"A` et de syntaxe par défaut. La seconde instruction définie l'élément `a` qui est de type `A`, la troisième instruction construit par produit directe l'élément `(f,a)` et rappelle sont type.

`f":"A"→"A`
`p(f) = x|->f(f(x))`

La première instruction déclare une fonction `f` de `A"→"A` et de syntaxe par défaut. La seconde instruction définie

1- Soit un nouvelle opérateur binaire qu'est la juxtaposition, appliqué à une variable `p` et à l'élément `f` qui est la forme nulaire de l'opérateur `f`. Le typage par inférence n'impose aucune contrainte à `p` qui est donc de type `Omega`. Et donc le type de l'opérateur de juxtaposition est `(Omega×A->A) ->A->A`

2- soit une nouvelle fonction `p` de syntaxe par défaut, prenant une fonction de `A"→"A` pour produire une fonction de `A"→"A`. Le typage par inférence donne à `p` le type `(A"→"A)"→"(A"→"A)` qui se normalise en `(A"→"A)"×"A → A` Et donc l'application `p` peut aussi s'appliquer à un couple `(f,a)` pour produire un élément de `A`.

`a":"A`
`p(f,a)==p(f)(a)`
`p(f,a)= f(f(a))`

Même exemple mais avec une autre syntatxe :

`f"|d:"A"→"A`
`p[f"|d"] = x|->xxf`
`a":"A`
`p(f,a)==p(f)(a)`
`p(f,a)= f(f(a))`

 

 

 

---

Considérons l'opérateur binaire `f` de type `A"×"B"→"C"×"D`. C'est aussi un opérateur unaire de même type, si l'unique argument transmis à `f` est de type `A"×"B`. Ainsi à travers la relativité des types, l'arité perd sa signification. Tout opérateur non-nullaire devient un opérateur unaire. Ainsi avec `a:A` et `b:B` nous avons :

`f(a,b)` `"="` `f("("a,b")")`

Et il paraît inutile d'en faire une distinction. Par currification, `f` est aussi un opérateur unaire de type équivalent, `A"→"(B"→"C"×"D)`. Ainsi nous avons :

`f(a,b)` `"="` `f(a)(b)`

Puis `f` peut être perçu comme un opérateur nullaire toujours de même type équivalent. Puis par distribution, `f` est aussi un couple d'opérateurs binaires de type équivalent, `(A"×"B"→"C)``"×"``(A"×"B"→"D)`. Ainsi nous avons :

`f =(f_C,f_D)`

`f(a,b) = (f_C,f_D)(a,b) = f_C(a,b)×f_D(a,b)`

La relativité des types fait apparaître une opération fondamentale qu'est l'application d'un opérateur unaire sur son argument qui se note par défaut par la juxtaposition d'un appel. Et le type de l'argument doit être compatible avec les différents types d'entrées possible de l'opérateur.

On prédéfinie l'opérateur d'appel noté `"@"` de syntaxe centrée avec une priorité syntaxique la plus haute, ainsi nous avons `f(x)= f"@"x`

La distributivité entraine :

`(f,g)"@"x = (f"@"x,g"@"x)`
`(f,g,h)"@"x = (f"@"x,g"@"x,h"@"x)`
...

La currification entraine :.

`f"@"(x,y)=(f"@"x)"@"y`
`f"@"(x,y,z)=((f"@"x)"@"y)"@"z`
...

 

---- 23 juin 2022 ----

 

Etant associatif on peut lui définir une forme unaire gauche `ļ` :

`x:Omega`
`y:Omega`
`z:Omega`
`ļ"¦g:"Omega"→Type"(ļ)`
`ļxyz "==" xyz`

`"Type"(x)` retourne le type de `x`. La définition du type est récurcive. Etant donné un type `K` d'opérateurs associatif :

`K = A"×"A"→"A`

On défini un opérateur unaire associé de type `L` comme suit :

`L = Omega"→"L`

Et la fonction qui transforme l'opérateur binaire associatif en son opérateur unaire associé :

m|-> (`ļ"¦g:"L `

 

 

il est possible d'overloader `f` que pour des types non-équivalent.

L'élément est défini à travers un langage et une structure. L'élément `x:A` est l'élément désigné par le nom `x` faisant partie du langage de la structure `A`. Un élément peut être défini à travers un langage et un contexte. Le contexte joue le rôle de structure anonyme. Ansi, si on évoque `x` sans autre précision, on évoque un élément de nom `x` dans le contexte en cours qui comprend un langage en cours. Et si on évoque `x:A` et `x:B`, alors l'élément `x` dans le contexte en cours désignera soit l'un ou soit l'autre (sachant qu'il peuvent être distincts ou égaux).

 

7) Blocs et contextes

Un bloc est délimité par des parenthèses et constitue un programme qui se comporte comme un opérateur. Exemple

{u,v | h(f(u,u),g(u,v)) }

---- 22 juin 2022 ----

 

Et il faut considérer l'éventualité d'un contexte d'entête. En effet, un niveau de parenthèse se comporte comme un bloc et ouvre un contexte, définissant le langage utilisé et qui est déterminé par l'opérateur qui utilise cette parenthèse comme entête d'appel.

 

 

4) Langage définissant les préscriptions syntaxiques

Chaque opérateur est identifié par un nom, une variante de parenthèse et un type sous forme normale. Le même nom d'opérateur peut être utilisé avec différentes variantes de parenthèses formant différents opérateurs. Cette faculté provient de la notation des physiciens qui identifie le résultat par le nom de la fonction et qui utilise un autre jeu de parenthèses si celui-ci- est calculé à partir d'un système différent d'arguments formant un système différents de coordonnées.

On définit le type de l'opérateur sans préciser sa syntaxe, mais en précisant la variantes de parenthèse si utilisé car celle-ci fait parti du nom de l'opérateur. Exemple :

f[] : A×A->B

<> : A×A->C

 

 

On définit un premier langage de prescription de la syntaxe. La syntaxe de l'opérateur est mentionné en notant la forme d'appel à l'aide du méta-symbole `"⦁"` représentant les arguments de l'opérateur obligatoirement mis dans l'ordre de la déclaration du type. Puis la déclaration de l'opérateur peut être suivi éventuellement d'une expression entre parenthèse composée de `"<"` ou `">"` et d'un opérateur, afin d'indiquer le niveau de priorité syntaxique juste au dessus ou juste en dessous de celle de l'opérateur mentionné. Par exemple :

`f["⦁,⦁"] : A"×"A"→"B`
` "⦁∗⦁" : A"×"A"→"A`

`"⦁∗⦁ (<∗") : B"×"B"→"A`
`"⦁+⦁" ("<∗":A"×"A"→"A) : A"×"A"→"A`

La première ligne définit un opérateur binaire de nom `"∗"` et de type `A"×"A"→"A`. La seconde ligne définit une deuxième opérateur binaire de même nom mais de `B"×"B"→"A` et de priorité syntaxique juste plus faible que l'opération `∗ : A"×"A"→"A`. La troisième ligne définit un opérateur binaire de nom `"+"` et de type `A"×"A"→"A` de priorité syntaxique juste plus faible que l'opération `∗ : A"×"A"→"A`. L'ordre des priorités syntaxiques du plus faible au plus fort est donc :

`"∗" : A"×"A"→"A`
`"+" : A"×"A"→"A`
`"∗" : B"×"B"→"A`

On définit un second langage respectant les principes syntaxiques qu'il définit, pouvant ainsi définir sa propre syntaxe lui-même. La syntaxe par défaut est enveloppée-gauche c'est à dire `f(x)`, `f(x,y)`, `f(x,y,z)`,...

 

Chaque opérateur possède une syntaxe parmis {collée-gauche, collé-droite, enveloppée-gauche, enveloppée-droite, centrée, enveloppée-centrée} Mais toutes les combinaisons ne sont pas autorisées.

---- 18 juin 2022 ----

 

5) Dualité type - valeur

On évite les langages verbeux, une variable `f` apparaissant la première fois est une déclaration d'une nouvelle variable qui au départ, est un objet le plus général possible de type Element, `Omega`.

Chaque objet a deux faces, l'une comme valeur, l'autre comme type tel un reflet. Un objet précis ne désignant qu'une valeur possible, désignera en tant que type, le type singleton le contenant. Le reflet d'une valeur singulière est le type singleton le contenant, un type qui pourra par la suite s'agrandire. Mais le reflet ne s'arrête pas là, il se reflète à son tour, faisant que l'objet possède une troisième face, un deuxième reflet, qui est mainetant une valeur, la valeur que représente ce type, une valeur définissant ce type. Et ainsi de suite. Réciproquement, un type `A` qui représente une structure, un ensemble de valeurs muni d'opérateurs, possède un contre-reflet comme valeur, qui représente une valeur quelconque de type `A`. Le contre-reflet d'une valeur `f` est le type défini par `f`. Le contre-contre-reflet d'une valeur `f` est une valeur qui représente une valeur quelconque de type définie par la valeur `f`.

L'objet `f` en tant que valeur est un opérateur le plus général qui soit, c'est à dire qu'il cumule toutes les arités sur toutes les configurations de types possibles et possède la syntaxe par défaut : `f, f"(.)", f"(.,.)", f"(.,.,.)", ....`.

Et son reflet, le type qu'il représente, est le plus général qui soit c'est à dire qu'il englobe tous les types possibles, et donc qu'il constitue le type Elément, `Omega`.

---- 18 juin 2022 ----

 

 

---- 18 juin 2022 ----

3.5) Opérateur multi-aire

Un opérateur multi-aire de type A^"*"->B est équivalent en l'union d'un opérateur nullaire de type B, d'un opérateur unaire de type A->B, et de la composition d'un opérateur unaire de type C->M avec un opérateur binaire associatif de type A×A->M.

L'opérateur binaire associatif admet implicitement une syntaxe multi-aire au moins binaire, et peut avoir une des 3 syntaxes suivantes : Il peut être de syntaxe enveloppée-gauche `(x,y,z,...)f`, ou enveloppée-droite `f(x,y,z,...)` ou centrée `xfyfz....`. cette dernière syntaxe explique pourquoi on a choisit une définition minimale. Puis on s'autorise différentes formes de parenthèses enveloppantes `f(x,y,z,...)`, `f[x,y,z,...]`, `f"<"x,y,z,...">"`, `f{x,y,z,...}`, le nom de l'opérateur étant `f()`, `f[]`, `f"<>"`, `f{}` où les caractères ouvrant `"<",{,(,[` et respectivement fermant `">",},),],}` font partie de la catégorie des caractères enveloppants. Et par défaut le nom sans caractère enveloppant `f` correspondra au nom `f()`.

Le type d'un opérateur multi-aire est par exemple :

`f:A^"∗"→"B`

`f:A^"+"→"B`

`f:A^"∧"→"B`

`A^"∗"` est l'ensemble des suites finies d'éléments de `A`, où `A^"+"` est l'ensemble des suites finies non-vides d'éléments de `A`, où `A^"∧"` est l'ensemble des suites finies non-vides et non-réduites à un seul élément, d'éléments de `A`.

 

3.5) Opérateur binaire associatif

Des considérations algèbriques font qu'un opérateur multi-aire est équivalent à un opérateur binaire associatif que l'on complète par un opérateur nullaire. Mais attention, cette définition de l'opérateur multi-aire est minimal : L'opérateur multi-aire appliqué à un seul élément le retourne à l'identique.

L'opérateur binaire associatif admet implicitement une syntaxe multi-aire au moins binaire, et peut avoir une des 3 syntaxes suivantes : Il peut être de syntaxe enveloppée-gauche `(x,y,z,...)f`, ou enveloppée-droite `f(x,y,z,...)` ou centrée `xfyfz....`. cette dernière syntaxe explique pourquoi on a choisit une définition minimale. Puis on s'autorise différentes formes de parenthèses enveloppantes `f(x,y,z,...)`, `f[x,y,z,...]`, `f"<"x,y,z,...">"`, `f{x,y,z,...}`, le nom de l'opérateur étant `f()`, `f[]`, `f"<>"`, `f{}` où les caractères ouvrant `"<",{,(,[` et respectivement fermant `">",},),],}` font partie de la catégorie des caractères enveloppants. Et par défaut le nom sans caractère enveloppant `f` correspondra au nom `f()`.

Le type d'un opérateur multi-aire est par exemple :

`f:A^"∗"→"B`

`f:A^"+"→"B`

`f:A^"∧"→"B`

`A^"∗"` est l'ensemble des suites finies d'éléments de `A`, où `A^"+"` est l'ensemble des suites finies non-vides d'éléments de `A`, où `A^"∧"` est l'ensemble des suites finies non-vides et non-réduites à un seul élément, d'éléments de `A`.

 

 

 


Dominique Mabboux-Stromberg