Le problème de la représentation des variables liées dans la syntaxe, et en particulier celui de la substitution évitant la capture, est bien connu et a plusieurs solutions: variables nommées avec équivalence alpha, indices de Bruijn, localement sans nom, ensembles nominaux, etc.
Mais il semble y avoir une autre approche assez évidente, que je n'ai pourtant pas vue utilisée nulle part. À savoir, dans la syntaxe de base, nous n'avons qu'un seul terme "variable", écrit par exemple , puis séparément nous donnons une fonction qui mappe chaque variable à un classeur dans la portée duquel elle se trouve. Donc, un -term comme
serait écrit , et la fonction mapperait le premier au premier et le second au second . C'est donc un peu comme les indices de Bruijn, mais au lieu d'avoir à "compter s" lorsque vous reculez pour trouver le liant correspondant, vous évaluez simplement une fonction. (Si cela représente une structure de données dans une implémentation, je penserais à équiper chaque objet à terme variable d'un simple pointeur / référence vers l'objet à terme de liant correspondant.)∙ λ ∙ λ λ
Évidemment, cela n'est pas judicieux pour écrire la syntaxe sur une page que les humains peuvent lire, mais les indices de Bruijn ne le sont pas non plus. Il me semble que cela a un sens mathématique parfait, et en particulier il rend la substitution évitant la capture très facile: il suffit de laisser tomber le terme que vous remplacez et de prendre l'union des fonctions de liaison. Il est vrai qu'il n'a pas de notion de "variable libre", mais alors (encore) les indices de Bruijn non plus; dans les deux cas, un terme contenant des variables libres est représenté par un terme avec une liste de classeurs "contextuels" devant.
Suis-je en train de manquer quelque chose et il y a une raison pour laquelle cette représentation ne fonctionne pas? Y a-t-il des problèmes qui le rendent tellement pire que les autres que cela ne vaut pas la peine d'être considéré? (Le seul problème auquel je peux penser en ce moment est que l'ensemble des termes (ainsi que leurs fonctions de liaison) ne sont pas définis par induction, mais cela ne semble pas insurmontable.) Ou existe-t-il réellement des endroits où il a été utilisé?
la source
Réponses:
Les réponses d'Andrej et de Łukasz soulèvent de bons points, mais je voulais ajouter des commentaires supplémentaires.
Pour faire écho à ce que Damiano a dit, cette façon de représenter la liaison à l'aide de pointeurs est celle suggérée par les réseaux de preuves, mais le premier endroit où je l'ai vu pour les termes lambda était dans un ancien essai de Knuth:
À la page 234, il a dessiné le diagramme suivant (qu'il a appelé une "structure d'information") représentant le terme :( λ y. λ z.yz) x
Ce type de représentation graphique des termes lambda a également été étudié de manière indépendante (et plus approfondie) dans deux thèses au début des années 1970, à la fois par Christopher Wadsworth (1971, Semantics and Pragmatics of the Lambda-Calculus ) et par Richard Statman (1974, Structural Complexity des preuves ). De nos jours, de tels diagrammes sont souvent appelés "graphiques λ" (voir par exemple cet article ).
Observez que le terme dans le diagramme de Knuth est linéaire , dans le sens où chaque variable libre ou liée se produit exactement une fois - comme d'autres l'ont mentionné, il y a des problèmes et des choix non triviaux à faire en essayant d'étendre ce type de représentation à des non - termes linéaires.
la source
Je ne sais pas comment votre fonction variable-liant serait représentée et dans quel but vous souhaitez l'utiliser. Si vous utilisez des pointeurs arrière, comme Andrej l'a noté, la complexité de calcul de la substitution n'est pas meilleure que l'alpha-renommage classique.
À partir de votre commentaire sur la réponse d'Andrej, j'en déduis que vous êtes dans une certaine mesure intéressé à partager. Je peux fournir quelques informations ici.
Dans un calcul lambda typé typique, l'affaiblissement et la contraction, contrairement à d'autres règles, n'ont pas de syntaxe.
Ajoutons une syntaxe:
Avec cette syntaxe, chaque variable est utilisée exactement deux fois, une fois où elle est liée et une fois où elle est utilisée. Cela nous permet de nous éloigner d'une syntaxe particulière et de considérer le terme comme un graphique où les variables et les termes sont des arêtes.
De la complexité algorithmique, nous pouvons maintenant utiliser des pointeurs non pas d'une variable à un liant, mais de liant à variable et avoir des substitutions dans un temps constant.
De plus, cette reformulation nous permet de suivre l'effacement, la copie et le partage avec plus de fidélité. On peut écrire des règles qui copient (ou effacent) de façon incrémentielle un terme tout en partageant des sous-termes. Il y a plusieurs façons de procéder. Dans certains contextes restreints, les victoires sont assez surprenantes .
Cela se rapproche des sujets des réseaux d'interaction, des combinateurs d'interaction, de la substitution explicite, de la logique linéaire, de l'évaluation optimale de Lamping, du partage des graphiques, de la logique lumineuse et autres.
Tous ces sujets sont très excitants pour moi et je me ferais un plaisir de vous donner des références plus spécifiques mais je ne sais pas si tout cela vous est utile et quels sont vos intérêts.
la source
Votre structure de données fonctionne, mais elle ne sera pas plus efficace que les autres approches, car vous devez copier chaque argument à chaque réduction bêta et vous devez faire autant de copies qu'il y a d'occurrences de la variable liée. De cette façon, vous continuez de détruire le partage de mémoire entre les sous-termes. Combiné avec le fait que vous proposez une solution non pure qui implique des manipulations de pointeurs, et est donc très sujette aux erreurs, cela ne vaut probablement pas la peine.
Mais je serais ravi de voir une expérience! Vous pouvez le prendre
lambda
et l'implémenter avec votre structure de données (OCaml a des pointeurs, ils sont appelés références ). Plus ou moins, il vous suffit de remplacersyntax.ml
etnorm.ml
avec vos versions. C'est moins de 150 lignes de code.la source
D'autres réponses traitent principalement des problèmes de mise en œuvre. Puisque vous mentionnez votre principale motivation comme faire des preuves mathématiques sans trop de comptabilité, voici le principal problème que je vois avec cela.
Lorsque vous dites "une fonction qui mappe chaque variable à un classeur dans la portée duquel elle se trouve": le type de sortie de cette fonction est sûrement un peu plus subtil que cela la fait sonner! Plus précisément, la fonction doit prendre des valeurs dans quelque chose comme «les liants du terme considéré» - c'est-à-dire un ensemble qui varie en fonction du terme (et n'est évidemment pas un sous-ensemble d'un ensemble ambiant plus grand de quelque manière utile). Donc, en substitution, vous ne pouvez pas simplement "prendre l'union des fonctions de liaison": vous devez également réindexer leurs valeurs, selon une correspondance entre les liants dans les termes d'origine et les liants dans le résultat de la substitution.
Ces réindexations devraient sûrement être «routinières», dans le sens où elles pourraient être raisonnablement soit balayées sous le tapis, soit bien emballées en termes de fonction ou de naturalité. Mais il en va de même pour la comptabilité impliquée dans le travail avec les variables nommées. Donc, dans l'ensemble, il me semble probable qu'il y aurait au moins autant de comptabilité impliquée dans cette approche qu'avec des approches plus standard.
Ceci mis à part, cependant, c'est une approche conceptuellement très attrayante, et j'aimerais que cela soit soigneusement élaboré - je peux bien imaginer que cela pourrait jeter un éclairage différent sur certains aspects de la syntaxe que les approches standard.
la source
Lazy.t
Dans l'ensemble, je pense que c'est une représentation sympa, mais cela implique une comptabilité avec des pointeurs, pour éviter de rompre les liens de liaison. Il serait possible de changer le code pour utiliser des champs mutables je suppose, mais l'encodage en Coq serait alors moins direct. Je suis toujours convaincu que cela est très similaire à HOAS, bien que la structure du pointeur soit explicite. Cependant, la présence de
Lazy.t
implique qu'il est possible qu'un code soit évalué au mauvais moment. Ce n'est pas le cas dans mon code car seule la substitution d'une variable par une variable peut se produire à laforce
fois (et pas d'évaluation par exemple).la source