# Logique combinatoire

En logique mathématique, la logique combinatoire est une théorie logique introduite par Moses Schönfinkel en 1920 lors d'une conférence et développée dès 1929 par Haskell Brooks Curry pour supprimer le besoin de variables en mathématiques, pour formaliser rigoureusement la notion de fonction et pour minimiser le nombre d'opérateurs nécessaires pour définir le calcul des prédicats à la suite de Henry M. Sheffer. Plus récemment, elle a été utilisée en informatique comme modèle théorique de calcul et comme base pour la conception de langages de programmation fonctionnels.
Le concept de base de la logique combinatoire est celui de combinateur qui est une fonction d'ordre supérieur ; elle utilise uniquement l'application de fonctions et éventuellement d'autres combinateurs pour définir de nouvelles fonctions d'ordre supérieur. Chaque combinateur simplement typable est une démonstration à la Hilbert en logique intuitionniste et vice-versa. On appelle ceci la correspondance de Curry-Howard

## Introduction

La logique combinatoire est fondée sur deux « opérations » de base (on dit aussi deux « combinateurs ») S et K que nous définirons plus loin ; plus précisément nous en définirons le comportement ou l'« intention », car la logique combinatoire est une approche de la logique qui montre plutôt comment marchent les choses que comment les objets peuvent être décrits, on dit alors que c'est une approche intentionnelle de la logique. Si l'on veut définir la fonction que nous appellerons C et qui prend trois paramètres et rend comme résultat le premier appliqué au troisième, le tout étant appliqué au second, on pourra l'écrire :
    C ≡ S ((S (K S) K) (S (K S) K) S) (K K)
qui, comme on le voit, ne comporte pas de variable. On pourra regretter que l'avantage de ne pas utiliser de variables se paie par la longueur des expressions et une certaine illisibilité. Aujourd'hui la logique combinatoire est surtout utilisée par les logiciens pour répondre positivement à la question « Est-il possible de se passer de variables ? » et par les informaticiens pour compiler les langages fonctionnels.
La logique combinatoire est un système de réécriture du premier ordre. C'est-à-dire qu'à la différence du lambda-calcul, il ne comporte pas de variables liées, ce qui permet une théorie beaucoup plus simple. Il n'a que trois opérateurs : un opérateur binaire et deux constantes.

### Le parenthésage

    Pour alléger l'écriture, la logique combinatoire supprime certaines parenthèses et adopte par convention l'associativité à gauche. En d'autres termes, (a b c d ... z) est un raccourci d'écriture pour (...(((a b) c) d) ... z).

## Les combinateurs de base

Le combinateur identité, noté I, est défini par
    I x = x.
Un autre combinateur, noté K, fabrique des fonctions constantes : (K x) est la fonction qui, pour tout paramètre, retourne x, autrement dit
    (K x) y = x
pour tous termes x et y. Comme en lambda-calcul, on associe les applications de gauche à droite, ce qui permet de supprimer des parenthèses, ainsi on écrit
    K x y = x.
Un autre combinateur, noté S, distribue le paramètre (ici z) aux applications composantes :
    S x y z = x z (y z)
S applique le résultat de l'application de x à z au résultat de l'application de y à z.
I peut être construit à partir de S et K, en effet :
    (S K K) x = S K K x
          = K x (K x)
          = x.
On décrète donc que I est un combinateur dérivé et que I = S K K et on décide de décrire tous les combinateurs à l'aide de S et K, ce qui est raisonnable car on peut montrer que cela suffit pour décrire « toutes » les fonctions d'une certaine forme$^,$.

## La réduction

En fait, les transformations fonctionnent comme des réductions et pour cela on les note →. On obtient donc les deux règles de réduction de base de la logique combinatoire.
       K x y → x,
       S x y z → x z (y z).

## Quelques combinateurs dérivés

* B ≡ S (K S) K. Le combinateur B correspond à l'opérateur de composition des fonctions habituellement noté « $\circ$ ». Son nom est dérivé du syllogisme Barbara. On a donc
       B x y z ≡ S (K S) K x y z
          → K S x (K x) y z
          → S (K x) y z
          → K x z (y z)
          → x (y z).
* C ≡ S (B B S) (K K) est un combinateur qui intervertit ses paramètres.
       C x y z ≡ S (B B S) (K K) x y z
          → B B S x (K K x) y z
          → B (S x) (K K x) y z
          → S x (K K x y) z
          → x z (K K x y z)
          → x z (K y z)
          → x z y
* W ≡ S I I. Le combinateur W permet de construire un autre combinateur à savoir W W, qui a la propriété de se réduire à lui-même. On a ainsi
       W W ≡ S I I (S I I)
          → I (S I I) (I (S I I))
          → S I I (I (S I I))
          → S I I (S I I) ≡ W W

## Le système de type

On peut associer un type à chacun des combinateurs. Le type d'un combinateur dit comment il prend en compte le type de ses paramètres pour produire un objet d'un certain type. Ainsi le combinateur I change son paramètre en lui-même ; si on attribue le type α à ce paramètre x, alors on peut dire que Ix a le type α et que I a le type α → α. Ici la flèche → désigne le constructeur de type fonctionnel, en gros α → α est le type de la classe des fonctions de α vers α, → a construit un nouveau type α → α à partir du type α.
K prend un paramètre, disons de type α et rend une fonction d'un paramètre de type β qui rend le premier paramètre, le type de cette dernière fonction est donc β → α et le type de K est ainsi α → (β → α), que l'on écrit α → β → α. S prend trois paramètres x, y et z ; donnons le type α au troisième paramètre z et le type γ au résultat final, le deuxième paramètre y prend un paramètre de type α et rend un paramètre de type disons β (son type est donc α → β), le premier paramètre x prend un paramètre de type α et rend une fonction de type β → γ, son type est donc α → (β → γ), que l'on écrit α → β → γ. Résumons-nous, on a z:α , y: β → α et x: α → β → γ et S x y z: γ, on en conclut que S a le type (α → β → γ) → (α → β) → α → γ.
Le résultat M N qui consiste à appliquer M à N est typable si M a un type fonctionnel, disons α → β et si N a pour type α. M N a alors pour type β.
Le type de B est (α → β) → (γ → α) → γ → β. On le voit soit en remarquant que B x y z →* x (y z), soit en appliquant la règle de composition à S (K S) K.
Le type de C est (α → β → γ) → β → α → γ, pour les mêmes raisons que celles invoquées pour B.
W en revanche n'est pas typable. On peut voir cela en se rappelant que S : (α → β → γ) → (α → β) → α → γ et I : (α → α). Si on applique S à I on doit avoir α = β → γ, puis si on applique S I à I on doit avoir α = β, donc β = β → γ. Cette équation n'a pas de solution. Donc S I I = W n'est pas typable.
En résumé:
       K : α → β → α
       S : (α → β → γ) → (α → β) → α → γ
       I : α → α
       B : (α → β) → (γ → α) → γ → β
       C : (α → β → γ) → β → α → γ

### Forte normalisation

Si M est un combinateur typé, alors toute chaine de réduction qui commence en M est finie. On appelle cette propriété la forte normalisation.

## La logique combinatoire et la correspondance de Curry-Howard

On constate que le modus ponens
    ${\frac {P\to Q\qquad P}{Q}}$
ressemble à la règle de conservation des types quand on applique un combinateur de type α → β à un combinateur de type α. Examinons aussi les deux premiers axiomes de la présentation à la Hilbert de la logique propositionnelle à savoir:
    K : P → Q → P
    S : (P → Q → R) → (P → Q) → P → R.
Rappelons qu'ils permettent de formaliser le calcul propositionnel intuitionniste. On remarque que le premier axiome est identique au type de K et que le deuxième axiome est identique au type de S si l'on remplace la proposition P par α, la proposition Q par β et la proposition R par γ. Cette correspondance entre proposition et type et entre combinateur et démonstration s'appelle la correspondance de Curry-Howard. Elle met en parallèle le système de déduction à la Hilbert pour la logique propositionnelle intuitionniste et la logique combinatoire qui ont été, notons-le, découverts indépendamment.

### Un exemple

La formule
       B : (α → β) → (γ → α) → γ → β
signifie (dans le langage de Rocq, par exemple) que B est une preuve de la formule propositionnelle (α → β) → (γ → α) → γ → β.
       B ≡ S (K S) K
fournit alors une preuve effective de la formule dans la théorie de Hilbert qui n'emploie que le modus ponens et les axiomes K et S.
Cela demande un petit travail de réécriture: Tout d'abord, on rétablit les parenthèses
       B ≡ (S (K S)) K
ensuite, on introduit l'opérateur ⇒
       B ≡ (S ⇒ (K ⇒ S)) ⇒ K
enfin, on emploie la notation postfixée :
       B ≡ K S K ⇒ S ⇒ ⇒
Alors cette formule donne les étapes de la démonstration dans le sens de la déduction. ⇒ dénote le recours au modus ponens ; K et S, l'utilisation des axiomes K et S. Plus, précisément Q ≡ I P ⇒ signifie que si I est la démonstration de P → Q, et P la démonstration de P, alors I P ⇒ est bien celle de Q. Malheureusement cette formule ne fournit pas les opérations de substitutions qui doivent être utilisées dans l'introduction des axiomes.
La notation préfixée,
       B ≡ ⇒ ⇒ S ⇒ K S K
représente le sens de la démonstration dans le langage de Rocq. Ici, les informations manquantes sont les formules des P employés dans les modus ponens.
Pour alléger l'écriture, on note
* P⁰=(P→Q)
* Q⁰=(R→P→Q)
* R⁰=(R→P)→(R→Q)
* MP(P) dénote l'utilisation du modus ponens avec la prémisse P
Le but est donc
* B = P⁰ → R⁰.
* MP(P⁰→Q⁰) remplace le but B par les 2 buts
*   * P¹→B
*   * P¹=P⁰→Q⁰
* MP(P⁰→Q⁰→R⁰) remplace le but P¹→B par les 2 buts
*   * P²→(P¹→B)
*   * P²=P⁰→Q⁰→R⁰
* S démontre P²→(P¹→B) car
*   * P²→(P¹→B) = (P⁰→Q⁰→R⁰)→(P⁰→Q⁰)→(P⁰→R⁰)
* MP(Q⁰→R⁰) remplace le but P² par les 2 buts
*   * P³→P²
*   * P³=Q⁰→R⁰
* K démontre P³→P² car
*   * P³→P² = (Q⁰→R⁰)→P⁰→(Q⁰→R⁰)
* S démontre P³ car
*   * P³ =(R→P→Q)→(R→P)→(R→Q)
* K démontre P¹ car
*   * P¹=P⁰→Q⁰=(P→Q)→R→(P→Q).

## Correspondance avec le λ-calcul

Toute expression de la logique combinatoire admet une expression du λ-calcul équivalente, et toute expression du λ-calcul admet une expression de la logique combinatoire équivalente.

### De la logique combinatoire vers le λ-calcul

Notons $[\![-]\!]_{\lambda }$ la traduction des combinateurs vers le λ-calcul, elle est définie par :
* si $x$ désigne une variable, $[\![x]\!]_{\lambda }=x$ ;
* $[\![{\mathsf {K}}]\!]_{\lambda }=\lambda x.\lambda y.x$ ;
* $[\![{\mathsf {S}}]\!]_{\lambda }=\lambda x.\lambda y.\lambda z.xz(yz)$ ;
* $[\![M\,N]\!]_{\lambda }=[\![M]\!]_{\lambda }\,[\![N]\!]_{\lambda }$, pour tous combinateurs $M$ et $N$.
Cette traduction est compatible avec la $\beta$-équivalence, c'est-à-dire que si $M=N$, alors $[\![M]\!]_{\lambda }=_{\beta }[\![N]\!]_{\lambda }$.

### Abstraction en logique combinatoire

Avant de définir la représentation du λ-calcul en logique combinatoire nous avons besoin de définir une abstraction dans la logique combinatoire. Si $M$ est un terme, on définit $[x].M$ qui va jouer le rôle de $\lambda x.M$.
       ${\begin{array}{llcl}(a)&{x].M&=&{\mathsf {K}}M\qquad {\textrm {si}}~x\notin FV(M)\\(b)&{x].x&=&{\mathsf {I}}\\(c)&{x].Ux&=&U\qquad {\textrm {si}}~x\notin FV(U)\\(d)&{x].UV&=&{\mathsf {S}}([x].U)([x].V)\qquad {\textrm {si}}~{\textrm {ni}}~(a)~{\textrm {ni}}~(c)~{\textrm {ne}}~{\textrm {s'appliquent}}.\end{array}}$

### Du λ-calcul à la logique combinatoire

On définit l'interprétation des termes $M$ du λ-calcul en termes $[\![M]\!]_{CL}$ de la logique combinatoire:
       ${\begin{array}{rcl}{\![x]\!]_{CL}&=&x\\{\![MN]\!]_{CL}&=&[\![M]\!]_{CL}[\![N]\!]_{CL}\\{\![\lambda x.M]\!]_{CL}&=&[x].[\![M]\!]_{CL}\end{array}}$
Cette interprétation n'est pas compatible avec la $\beta$-équivalence : il existe des lambda-termes $M$ et $N$ tels que $M=_{\beta }N$ et $[\![M]\!]_{CL}\neq [\![N]\!]_{CL}$, par exemple $[\![{\mathsf {SK}}]\!]_{\lambda }$ et $[\![{\mathsf {KI}}]\!]_{\lambda }$ :
$[\![{\mathsf {SK}}]\!]_{\lambda }=(\lambda x.\lambda y.\lambda z.xz(yz))(\lambda t.\lambda u.t)=_{\beta }\lambda y.\lambda z.z=_{\beta }(\lambda x.\lambda y.x)(\lambda z.z)=[\![{\mathsf {KI}}]\!]_{\lambda }$
Mais $[\![[\![{\mathsf {SK}}]\!]_{\lambda }]\!]_{CL}={\mathsf {SK}}$, $[\![[\![{\mathsf {KI}}]\!]_{\lambda }]\!]_{CL}={\mathsf {KI}}$ et ${\mathsf {SK}}\neq {\mathsf {KI}}$.

### Exemple

Le combinateur de point fixe de Turing, noté $\Theta$ a pour expression en λ-calcul $(\lambda x.\lambda y.y\,(x\,x\,y))\,(\lambda x.\lambda y.y\,(x\,x\,y))$. On peut alors calculer :
       ${\begin{array}{rcl}[\![\lambda y.y\,(x\,x\,y))]\!]_{CL}&=&[y].[\![y\,(x\,x\,y))]\!]_{CL}\\&=&S([y].[\![y]\!]_{CL})([y].[\![xxy]\!]_{CL})\\&=&SI([\![xx]\!]_{CL})\\&=&SI(xx)\end{array}}$
puis
       ${\begin{array}{rcl}[\![\lambda x.\lambda y.y\,(x\,x\,y))]\!]_{CL}&=&[x].[\![\lambda y.y\,(x\,x\,y))]\!]_{CL}\\&=&[x].SI(xx)\\&=&S([x].SI)([x].xx)\\&=&S(K(SI))(S([x].x)([x].x)\\&=&S(K(SI))(SII)\end{array}}$
On définit alors deux combinateurs A et Θ
A:=S (K (S I)) (S I I)
Θ: =A A
Θ est un combinateur de point fixe.
On observe que, qu'il s'agisse du λ-terme ou de sa traduction en tant que combinateur, on a $\Theta x~{\stackrel {*}{\longrightarrow }}~x(\Theta x)$.

## Problèmes indécidables en logique combinatoire

Une forme normale est un combinateur dans lequel aucune règle de réduction n'est applicable. Déterminer si un combinateur peut se réduire en combinateur en forme normale est indécidable. De même, déterminer si deux combinateurs distincts sont égaux est indécidable. Ces deux résultats sont à rapprocher de l'indécidabilité de l'équivalence de deux termes dans le lamda-calcul, ou de l'indécidabilité de l'existence d'une forme normale pour un terme donné.