Saisir un mot clé:
 
 

lambda-calcul

Ce site est un miroir du site http://fr.wikipedia.org/wiki/Accueil
ok

google_ad_height = 15; google_ad_format = "728x15_0ads_al"; google_ad_channel =""; google_color_border = "f9f9f9"; google_color_bg = "FFFFFF"; google_color_link = "0000FF"; google_color_url = "008000"; google_color_text = "000000"; //-->

Un article de Wikipedia.y-project.com.

Le lambda-calcul (ou ?-calcul) est un langage de programmation théorique inventé par Alonzo Church dans les années 1930. Ce langage a eu autant d'importance que les machines de Turing dans la théorie de la calculabilité. Il s'agit d'un modèle de calcul, c'est à dire une formalisation de la notion de calcul. Or on peut simuler la normalisation des ?-termes à l'aide d'une machine de Turing, et simuler une machine de Turing par des ?-termes. Ces deux modèles sont donc équivalents (ou Turing-équivalents). Or la thèse de Church-Turing dit que tout algorithme peut être calculé par une machine de Turing, donc par l'équivalence tout algorithme peut être calculé par le lambda-calcul. C'est ainsi que le premier langage de programmation est né.

Ce langage n'est cependant pas explicitement utilisé tel quel en pratique car même s'il peut calculer tout ce qui est calculable, le temps de calcul n'est pas optimal. Cependant des lambda calculs typés sont à l'origine des langages de programmation fonctionnels comme Lisp ou Caml car la théorie des types apporte une sémantique forte et sûre.

La lambda-calcul est apparenté à la logique combinatoire dûe à Haskell Curry.

Sommaire

[] Syntaxe

Les objets du lambda-calcul sont les lambda-termes. Il en existe trois sortes :

  • les variables : <math>x</math>, <math>y</math>, ...
  • les applications : si <math>u</math> et <math>v</math> sont des lambda-termes <math>uv</math> est aussi un lambda-terme. On peut alors voir <math>u</math> comme une fonction et <math>v</math> comme un argument, <math>uv</math> étant alors l'image de <math>v</math> par la fonction <math>u</math>.
  • les abstractions : si <math>x</math> est une variable et <math>u</math> un lambda-terme alors <math>\lambda x.u</math> est un lambda-terme. Intuitivement, <math>\lambda x.u</math> est la fonction qui à <math>x</math> associe <math>u</math>.

Pour des raisons pratiques, on introduit parfois également des constantes et des opérations primitives (par exemple les entiers naturels et les opérations + et * usuelles). Ainsi, la fonction qui, à x associe x + 2 pourra être représentée par <math>\lambda x.x+2</math>. Du point de vue du lambda calcul « pur », ces primitives se comportent comme des variables, si ce n'est qu'on peut leur donner des règles de calcul spéciales (cf. ci-dessous).

[] Notations

[] Arguments/Abstraction multiples

Vous aurez remarqué qu'un lambda ne prend qu'un seul argument mais on peut contourner cet obstacle de la façon suivante : la fonction qui au couple (x, y) associe u sera considérée comme une fonction qui, à x, associe une fonction qui, à y, associe u. Elle est donc notée : ?x.(?y.u). Au point de vue de la notation on peut aussi écrire ?x.?y.u ou ?x?y.u ou tout simplement ?xy.u. Par exemple, la fonction qui, au couple (x, y) associe x + y pourra être notée <math>\lambda x.\lambda y.x+y</math> ou plus simplement <math>\lambda xy.x+y</math>.

[] Parenthésage

Du point de vues du parenthésage x1 x2 ... xn = ((x1 x2) ... xn)

Pour le parenthésage, il y'a plusieurs formalisme:

  1. <math>\Lambda \to Var | \lambda Var \Lambda | \Lambda \Lambda | (\Lambda)</math>

Comme expliqué ci-dessus, un parenthésage à gauche. (On conviendra d'utiliser cette convention ici)

  1. <math>\Lambda \to Var | \lambda Var \Lambda | \Lambda (\Lambda)</math>

Qui correspond à un parenthésage a droite.

[] Contraction

On peut contracter certaines parties d'un lambda-terme : plusieurs applications successive d'un même terme/variable peuvent etre "factorisées"

<math>\lambda x. f f f f x</math> est equivalent à : <math>\lambda x. f^ x</math>

[] Variables libres

Dans les expressions mathématiques il y a deux types de variables : les variables libres et les variables liées (ou muettes). Les variables liées sont locales à un morceau de l'expression. On peut les renommer à volonté sans changer la signification de l'expression.

Par exemple dans <math>f(x) = \int_a^b {x + y~dy}</math>, x est libre, mais y est liée (par le dy). Ceci est la même expression que <math>f(x) = \int_a^b {x + z~dz}</math> car y était un nom local, tout comme l'est z. Par contre <math>f(x) = \int_a^b {z + y~dy}</math> ne correspond pas à la même expression car le x est libre. D'ailleurs la notation f(x) précise que l'ensemble de ses variables (libres) est .

Tout comme l'intégrale lie la variable d'intégration, le ? lie la variable qui le suit.


Exemples: dans <math>\lambda x.xy</math> x est lié, et y libre. On peut réecrire ce terme ainsi : <math>\lambda t.ty</math>.

Plus compliqué <math>\lambda xyzt. z(xt)ab(zsy)</math> est aussi équivalent à <math>\lambda wjit. i(wt)ab(isj)</math>


On définit l'ensemble VL(t) des variables libres d'un terme t par récurrence :

  • si x est une variable alors VL(x) =
  • si u et v sont des lambda-termes alors VL(u v) = VL(u) ? VL(v)
  • si x est une variable et u un lambda-terme alors VL(?x.u) = VL(u) -

Si un lambda-terme n'a pas de variables libres alors on dit qu'il est clos.

[] Principes

L'outil le plus important pour le calcul est la substitution. Cette opération permet de remplacer une variable par un terme. Grâce à ce mécanisme, les réductions vont pouvoir « calculer » le terme.

La substitution dans un lambda terme t d'une variable x par un terme u est notée t[x := u]. On la définit par récurrence sur t :

  • si t est une variable alors t[x := u]=u si x=t et t sinon
  • si t = v w alors t[x := u] = v[x := u] w[x := u]
  • si t = ?y.v alors t[x := u] = ?y.(v[x := u]) si x<math>\neq</math>y et t sinon

Dans le dernier cas on fera attention à ce que y ne soit pas une variable libre de u. En effet, elle serait alors « capturée » par le lambda externe. Si c'est le cas on renomme y et toutes ses occurrences dans v. C'est l'alpha-conversion, qui permet d'identifier <math>\lambda</math>y.v et <math>\lambda</math>z.v[y := z]. Rappelez vous que les variables sous les lambdas sont des variables dites « muettes ».

Exemples :

  • <math>\lambda</math>x.xy[y := a] = <math>\lambda</math>x.xa
  • <math>\lambda</math>x.xy[y := x] = <math>\lambda</math>z.zx(et non <math>\lambda</math> x.xx, qui est totalement différent)

Remarque : l'alpha-conversion, qui permet d'identifier <math>\lambda</math>y.v et <math>\lambda</math>z.v[y := z] doit être définie avec précaution et avant la substitution. En effet la substitution de z dans v n'est pas encore disponible et pose les mêmes problèmes de capture. Ainsi dans le terme <math>\lambda</math>x.<math>\lambda</math>y.xy<math>\lambda</math>z.z, on ne pourra pas renommer x en y (on obtiendrait <math>\lambda</math>y.<math>\lambda</math>y.yy<math>\lambda</math>z.z) par contre on peut renommer x en z.

Les définitions formelles et correctes de la alpha-conversion et de la substitution se trouvent dans le livre de J.-L. Krivine par exemple, mais une bonne compréhension de la notion de variables liées, habituelles en mathématiques, suffit largement pour comprendre le lambda-calcul.

Définie ainsi la substitution est un mécanisme externe au lambda-calcul, on dit aussi qu'il fait partie de la méta-théorie. A noter que certains travaux visent à introduire la substitution comme un mécanisme interne au lambda-calcul, conduisant à ce qu'on appelle les calculs de substitutions explicites.

[] Réductions

Les termes sont des arbres avec des n?uds binaires (applications), des n?uds unaires (les ?-abstractions) et des feuilles (les variables). Les réductions permettent de l'arbre, cependant l'arbre n'est pas forcément « plus petit » après l'opération. Exemple : si vous réduisez (?x.xxx)(?x.xxx) vous obtiendrez (?x.xxx)(?x.xxx)(?x.xxx). La réduction tient son nom du fait que l'on va réduire un morceau du terme : le rédex.

On appelle rédex un terme de la forme (<math>\lambda</math>x.u) v. On définit alors la bêta-contraction (ou réduction) de (<math>\lambda</math>x.u) v comme u[x := v]; on note ? la relation de réduction: ainsi (<math>\lambda</math>x.u) v donne u[x := v]. Intuitivement, (<math>\lambda</math>x.u)v est la valeur de la fonction <math>\lambda</math>x.u appliquée à la variable v. u étant l'image de x par la fonction (<math>\lambda</math>x.u), l'image de v est obtenue en substituant dans u, x par v. Inversement u[x := v] est appelé le contractum de (<math>\lambda</math>x.u) v.

Exemple de réduction :

(<math>\lambda</math>x.xy)a donne xy[x := a] = ay

On note ?* la fermeture réflexive transitive de la relation ? de contraction (?* est appelée la bêta-réduction) et =? sa fermeture réflexive symétrique et transitive (appelée bêta-conversion ou bêta-equivalence).

La ?-conversion permet de faire une "marche arrière" à partir d'un terme. Cela permet, par exemple, de retrouver le terme avant une ?-réduction. Passer de <math>x</math> à <math>(\lambda y.y)x</math>.

On peut écrire <math> M =_M'\mbox{ si }\exists N_,...,N_\mbox{ tels que }M = N_, M'=N_ \mbox{ et } N_ \to N_ \mbox{ ou } N_ \to N_</math>

On va ainsi voyager par des réductions, et par des retours en arrière de M à M'.

On utilise également assez fréquemment une autre opération, appelée êta-réduction (ou son inverse la êta-expansion), définie ainsi : <math>\lambda</math>x.ux ?? u, lorsque x n'apparaît pas libre dans u. En effet, ux s'interprête comme l'image de x par la fonction u. <math>\lambda</math>x.ux s'interprête alors comme la fonction qui, à x, associe l'image de x par u, donc comme la fonction u elle-même.

Enfin, si on s'est donné des primitives, on peut fixer leur comportement calculatoire au moyen des règles de delta-réduction. Par exemple, si on s'est donné les entiers et + comme termes supplémentaires, les tables d'addition serviront de delta-règles. Comme les primitives sont par définition complètement étrangères au lambda-calcul, leurs règles de calcul peuvent a priori adopter n'importe quelle forme. Toutefois, si on veut étendre les propriétés mentionnées ci-dessous au cas d'un calcul avec des primitives, on est amené à faire quelques hypothèses sur les règles ajoutées.

[] La normalisation : notion de calcul

Le calcul d'un lambda-terme peut être défini par sa réduction. Sa forme originale est l'énoncé et la forme normale est le résultat.

Un lambda-terme t est dit en forme normale si aucune bêta-contraction ne peut lui être appliquée, c'est-à-dire si t ne contient aucun rédex. Ou encore, s'il n'existe aucun lambda-terme u tel que t ? u.

Exemple: <math>\lambda x.y(\lambda z.z(yz)) </math>

On dit qu'un lambda-terme t est normalisable s'il existe une forme normale u et une suite de lambda-termes t1, ..., tn telle que t = t1, u = tn et pour tout i (1 ? i < n) ti ? ti+1 ou ti+1 ? ti. Un tel u est appelé la forme normale de t.

On dit qu'un lambda terme t est fortement normalisable si toutes les réductions à partir de t sont finies.

Exemples:

  1. Voici l'exemple le plus classique de lambda-terme non calculable :
    <math> \Omega = (\lambda x.xx)(\lambda x.xx) = \Delta \Delta</math>
    Le lambda terme <math>\Omega</math> n'est pas calculable car il boucle infiniment sur lui-même si on tente de le réduire.
    <math> (\lambda x.xx)(\lambda x.xx) \to (\lambda x.xx)(\lambda x.xx)</math>. (<math>(\lambda x.xx)</math> s'applique sur le premier argument de <math>(\lambda x.xx)</math>, les deux x suivant le point prennent donc cette valeur, et on retrouve le terme de départ.)
  2. <math>(\lambda x.x)((\lambda y.y)z)</math> est un lambda-terme fortement normalisable.
  3. <math>(\lambda x.y)(\Delta \Delta)</math> est normalisable, mais pas fortement normalisable.

Si un terme est fortement normalisable, alors il est normalisable.

Théorème de Church-Rosser : soient t et u deux termes tels que t =? u. Il existe un terme v tel que t ?* v et u ?* v.

Théorème du losange (ou de confluence) : soient t, u1 et u2 des lambda-termes tels que t ?* u1 et t ?* u2. Alors il existe un lambda-terme v tel que u1 ?* v et u2 ?* v.

Souvent on appelle ce dernier théorème Théorème de Church-Rosser car les deux théorèmes sont équivalents.

Un lambda-terme est dit calculable s'il admet une forme normale. Grâce au Théorème de Church-Rosser on peut facilement montrer l'unicité de la forme normale ainsi que la cohérence du lambda-calcul (c?est-à-dire qu'il existe au moins deux termes distincts non bêta-convertibles).

[] Différents Lambda-calculs

Le lambda-calcul défini ci-dessus n'est qu'une trame syntaxique appelée à être complétée par une sémantique qui permet de donner une signification à ce que l'on fait par ce lambda-calcul. On peut distinguer deux grandes classes de lambda-calculs : les lambda-calculs non typés et les lambda-calculs typés. La différence est qu'avec les types on va restreindre les termes valides. C?est-à-dire que généralement on va chercher à enlever les termes qui ne sont pas normalisables. Le but est d'avoir un lambda-calcul qui vérfie les propriétés de Church-Rosser et de normalisation (voire normalisation forte).

Même si en restreignant les termes valides par les types on garde généralement les plus intéressants. De plus grâce à l'isomorphisme de Curry-Howard on peut relier un lambda calcul à une logique et par là un terme correspond à une preuve. Intuitivement c'est très fort parce que cela permet d'associer à une preuve mathématique un programme informatique. C'est sur cela que se base une partie des prouveurs automatiques et semi-automatiques.

[] Le Lambda-calcul non typé

On va utiliser des codages pour créer les objets usuels de l'informatique. Grâce à ces objets on peut tout calculer car on peut simuler une machine de Turing. Et grâce aux théorèmes de la théorie de la calculabilité on en déduit que le lambda-calcul est un modèle universel de calcul.

[] Les booléens

Pour faire de vrais calculs on va faire des codages. On définit le booléen vrai par ?ab.a et faux par ?ab.b

Nous remarquons que :

vrai x y ?* x

et que :

faux x y ?* y

Nous en déduisons un programme pour ifthenelse : ?buv.buv

On pourra vérifier que :

ifthenelse vrai x y ?* x

et que :

ifthenelse faux x y ?* y

À partir de là nous avons aussi un lambda-terme pour les opérations booléennes classiques :

non = ?b.ifthenelse b faux vrai
et = ?ab.ifthenelse a b faux ou bien ?ab.ifthenelse a b a
ou = ?ab.ifthenelse a vrai b ou bien ?ab.ifthenelse a a b

[] Les entiers

Nous allons utiliser les Entiers de Church. n = ?fx.f(f(...(f x)...)) = ?fx.fnx avec n f. Par exemple 0 = ?fx.x, 3 = ?fx.f(f(f x)).

[] Quelques fonctions

Il y a deux manières de coder la fonction successeur. Soit en ajoutant un f en tête soit en queue. Au départ nous avons n = ?fx.fn x et nous voulons ?fx.fn+1 x. Il faut pouvoir rajouter un f soit au début des f « sous » les lambdas soit à la fin.

  • Si nous choisissons de le mettre en tête, il faut pouvoir entrer « sous » les lambdas. Pour cela il faut créer des redex donc si n est notre entier il faut faire n f x, ce qui donne fn x. En mettant un f en tête nous avons presque terminé : f (n f x) ? f(fn x) = fn+1 x. Il nous faut maintenant refaire les lambdas pour reconstruire un entier de Church : ?fx.f (n f x) = ?fx.fn+1 x (nous aurions bien sûr pu prendre d'autres noms de variables que f et x à l'étape précédente et donc nous aurions gardé ces noms ici). Enfin pour avoir la « fonction » successeur il faut bien entendu prendre un entier en paramètre, donc rajouter un lambda. Nous obtenons ?nfx.f(n f x). Le lecteur pourra vérifier que (?nfx.f(n f x)) 3 = 4, avec 3 = ?fx.f(f(f x))) et 4 = ?fx.f(f(f(f x)))).
  • Si par contre nous voulions mettre le f en queue, c'est légèrement plus rusé. Lorsque nous avons « décortiqué » l'entier pour enlever les lambdas, nous avons appliqué des simples variables à nos lambdas. Rien ne nous empêche de mettre autre chose. Or ici nous voulons bien f x à la place de x : nous allons donc faire ceci : n f (f x), ce qui se réduit à fn (f x) = fn+1 x. On n'a plus qu'à refaire l'emballage comme dans le cas précédent et on obtient ?nfx.n f (f x). La même vérification pourra être faite.

Les autres fonctions sont construites avec le même principe, en appliquant ce qu'il faut dans f et x. Par exemple l'addition en « concaténant » les deux lambda-termes : ?npfx.n f (p f x).

Pour coder la multiplication c'est un peu plus futé : on va utiliser le f pour « propager » une fonction sur tout le terme : ?npfx.n (p f) x

L'exponentielle n'est pas triviale contrairement à ce que son écriture laisse penser, et lors de la réduction on est obligé de renommer les variables : ?np.p n

Le prédécesseur et la soustraction ne sont pas simples non plus. On en reparlera plus loin.

On peut définir le test à 0 ainsi : if0thenelse = ?nab.n (\d.faux) vrai

[] Les itérateurs

Tout cela n'est pas très intuitif alors pour pouvoir coder des algorithmes on définit la fonction d'itération sur les entiers : iterate = ?nuv.n u v

Le truc c'est que v est le cas de base et u une fonction sur le cas de récurrence.

Par exemple l'addition qui provient de ces équations

  • add(0, p) = p
  • add(n+1, p) = (n+p) + 1

On va donc programmer par itération avec le successeur sur le cas de base p. Le lambda-terme est donc ?np.iterate n successeur p. On remarquera que add n p ?* n successeur p.

[] Les couples

On peut coder des couples par c = ?z.z a b où a est le premier élément et b le deuxième. On notera ce couple (a,b). Pour accéder aux deux parties on utilise ?1 = ?c.c (?ab.a) et ?2 = ?c.c (?ab.b). On reconnaîtra les booléens vrai et faux dans ces expressions et on laissera le soin au lecteur de réduire ?1(?z.z a b)

[] Les listes

On peut remarquer qu'un entier est une liste qui ne contient pas de clé. En rajoutant une information on peut construire les listes d'une manière analogue aux entiers : [a1 ; ... ; an] = ?gy. g a1 (... (g an y)...)

[] Les itérateurs sur les listes

De la même manière qu'on a introduit une itération sur les entiers on introduit une itération sur les listes. la fonction liste_it se définit par ?lxm.l x m comme pour les entiers. Le concept est à peu près le même mais il y a des petites nuances. Nous allons voir par un exemple.

exemple : La longueur d'une liste est définie par

  • longueur ([]) = 0
  • longueur (x :: l) = 1 + longueur l

x :: l est la liste de tête x et de queue l (notation ML). Cela se code par ?l.liste_it l (?ym.add (?fx.f x) m) (?fx.x) ou tout simplement ?l.l (?ym.add 1 m) 0

[] Les arbres

Le principe de construction des entiers, des couples et des listes se généralisent aux arbres binaires :

  • feuille: nil = ?gy.y
  • constructeur de n?ud: node(b,c) = ?gy.g b c
  • itérateur: arbre_it = ?axm.a x m

On peut alors définir la fonction qui calcule la profondeur d'un arbre : longueur_arbre = ?a.arbre_it a (\ybc.add b c) 0.

[] Le prédécesseur

Pour définir le prédécesseur avec les entiers de Church, il faut ruser et utiliser les couples. L'idée est de reconstruire le prédécesseur par itération : pred = ?n.?1 (iterate n (?c.(?2 c,successeur (?2 c))) (0,0)). On note en passant que le prédécesseur sur les entiers naturels n'est pas spécialement défini en 0. On a ici arbitrairement adopté la convention qu'il vaudrait 0.

On vérifie par exemple que pred 3 ?* ?1 (iterate 3 (?c.(?2 c,successeur (?2 c))) (0,0)) ?* ?1 (2,3) ?* 2.

On en déduit que la soustraction est définissable par : sub = ?np.iterate p pred n avec la convention que si p est plus grand que n, alors sub n p vaut 0.

[] La récursion

En combinant prédécesseur et itérateur, on obtient un récurseur. Celui-ci se distingue de l'itérateur par le fait que la fonction qui est passée en argument a accès au prédécesseur.

rec = ?nfx.?1 (n (?c.(f (?2 c) (?1 c),successeur (?2 c))) (x,0))

[] Le combinateur de point fixe

Le combinateur de point fixe permet de faire des boucles infinies. Ceci est pratique pour programmer des fonctions qui ne s'expriment pas simplement par itération, telle que le pgcd, et c'est surtout nécessaire pour programmer des fonctions partielles.

Le combinateur de point de fixe le plus simple est : Y = ?f.(?x.f(x x))(?x.f(x x))

On vérifie que Y g ?* g(Y g) quelque soit g. Grâce au combinateur de point de fixe, on peut par exemple définir une fonction qui prend en argument une fonction et teste si cette fonction argument renvoie vrai pour au moins un entier: teste_annulation = ?g.Y (?fn.ou (g n) (f (successeur n))) 0.

Par exemple, si on définit la suite alternée des booléens vrai et faux : alternate = ?n.iterate n non faux, alors, on vérifie que : teste_annulation alternate ?* ou (alternate 0) (Y (?fn.ou (alternate n) (f successeur n)) (successeur 0)) ?* ou (alternate 1) (Y (?fn.ou (alternate n) (f successeur n)) (successeur 1)) ?* vrai.

On peut aussi définir le pgcd : pgcd = Y (?fnp.if0thenelse (sub p n) (if0thenelse (sub n p) p (f n (sub p n))) (f p (sub n p))).

[] Connexion avec les fonctions partielles récursives

Le récurseur et le point fixe sont des ingrédients clés permettant de montrer que toute fonction partielle récursive est définissable en ?-calcul lorsque les entiers sont interprétés par les entiers de Church. Réciproquement, les ?-termes peuvent être codés par des entiers et la réduction des ?-termes est définissable comme une fonction (partielle) récursive. Le ?-calcul est donc un modèle de la calculabilité

[] Le Lambda-calcul simplement typé

On introduit des types simples sur les termes, et on n'accepte que les termes bien typés. Outre un souci de clarté et de compréhension, cela permet d'avoir la normalisation forte, c'est-à-dire que pour tous les termes, toutes les réductions aboutissent à une forme normale (qui est unique pour chaque terme de départ). Autrement dit, tous les calculs menés dans ce contexte terminent. En contrepartie, le pouvoir expressif de ce calcul est très limité (ainsi, l'exponentielle ne peut y être définie, ni même la fonction <math>n\rightarrow 2^n</math>).

Plus formellement, les types sont construit de la manière suivante:

  • un type de base <math>\iota</math> (si on a des primitives, on peut aussi se donner plusieurs types de bases, comme les entiers, les booléens, les caractères, etc. mais cela n'a pas d'incidence au niveau de la théorie).
  • si <math>\tau_1</math> et <math>\tau_2</math> sont des types, <math>\tau_1\rightarrow\tau_2</math> est un type.

Intuitivement, le second cas représente le type des fonctions acceptant un élément de type <math>\tau_1</math> et renvoyant un élément de type <math>\tau_2</math>.

Un contexte <math>\Gamma</math> est un ensemble de paires de la forme <math>(x,\tau)</math> où <math>x</math> est une variable et <math>\tau</math> un type. Un jugement de typage est un triplet <math>\Gamma\vdash t:\tau</math> (on dit alors que <math>t</math> est bien typé dans <math>\Gamma</math>), défini récursivement par:

  • si <math>(x,\tau)\in\Gamma</math>, alors <math>\Gamma\vdash x:\tau</math>.
  • si <math>\Gamma\cup (x,\tau_1)\vdash u:\tau_2</math>, alors <math>\Gamma\vdash \lambda x.u:\tau_1\rightarrow\tau_2</math>.
  • si <math>\Gamma\vdash u:\tau_1\rightarrow\tau_2</math> et <math>\Gamma\vdash v:\tau_1</math>, alors <math>\Gamma\vdash u v:\tau_2</math>

Si on s'est donné des primitives, il faut leur donner un type (via <math>\Gamma</math>). Dans le cas de la règle de l'abstraction, l'ajout de <math>x</math> masque une éventuelle occurrence précédente de la variable dans <math>\Gamma</math>.

[] Les Lambda-calculs typés d'ordres supérieurs

Le lambda-calcul simplement typé est trop restrictif pour pouvoir calculer toutes les fonctions dont on a besoin habituellement en mathématiques et donc dans un programme informatique. Un autre modèle permet de calculer toutes les fonctions calculables dans un temps plus ou moins acceptable : la récursion primitive. Ce système permet de calculer la plupart des fonctions calculables par une machine de Turing. Le problème est au niveau de la complexité car on n'a pas de bons algorithmes de calcul et donc de manière efficace de calculer. Ce problème est réglé avec le Système T de Gödel qui fusionne la récursion primitive et le lambda-calcul simplement typé. Dans ce système on n'a pas seulement de nouveaux algorithmes mais aussi de nouveaux programmes comme la fameuse fonction d'Ackermann qui est la plus petite fonction non primitive récursive. Cependant elle est hors de portée des ordinateurs actuels car on n'aurait même pas assez de mémoire pour stocker le résultat de la fonction appliquée aux arguments 5 5 par exemple.

Bien que ce modèle permette de calculer tout ce que l'on veut avec des algorithmes corrects, théoriquement on peut faire beaucoup mieux. Notamment par l'introduction des variables de type. Cela augmente la complexité (du point de vue compréhension et non informatique) du système mais augmente considérablement le pouvoir expressif. Pour les lambdas calculs typés du second ordre on peut faire des termes qui dépendent de types, des types qui dépendent de termes et des types qui dépendent de types. En faisant la combinaison de chacun on obtient huit lambda-calculs que Barendregt modélise sous forme de cube. L'extrémité du cube opposée à celle du lambda-calcul simplement typé est le calcul des constructions dû à Thierry Coquand, et a donné naissance au système coq.

[] Références


DernierMirror  
Le Texte ci-dessus est disponible sous GNU Free Documentation License.
La source est wikipedia http://fr.wikipedia.org/wiki/lambda-calcul
Home

Données
A la une
Articles
Formatons en lignes
Téléchargement
Licence GNU
Encyclopedie
Portail logiciels libres

Partenaires

beyrouthsurseine.com
Sonnerie & Logos
Photos-Video
Ringtones-Sonnerie
Actualite.org
Terrain tennis

  
Décembre 2008
L
M
M
J
V
S
D
1234567
891011121314
15161718192021
22232425 262728
293031
     
Tous les Logos et Marques sont déposés, les commentaires sont sous la responsabilité de ceux qui les ont publiés, le reste © technicmania.com