Premessa

Ciao! Se è la prima volta che capiti su questo sito, ti consiglio di consultare la pagina principale di questo cosiddetto Giardino Digitale per scoprire meglio cos’è e come navigarlo.

Lo stato di questa nota è al momento: 🟡 Incompleta.


Con l’introduzione a variabili, astrazioni, applicazioni, operazioni come la -riduzione e metodi come il currying, abbiamo tutto quel che ci serve per trasformare il -calcolo in un linguaggio di programmazione Turing-completo (tramite l’implementazione di strumenti fondamentali nella programmazione come cicli, tipi di dati basilari e strutture dati più complicate), esattamente come dimostrato nella tesi di Church-Turing.

Per fare ciò, Church ha ideato una sua codifica, la codifica di Church, che permette di esprimere coerentemente (dal punto di vista semantico) tutti i costrutti fondamentali della computazione esclusivamente mediante il -calcolo.

Definizione: codifica di Church

La codifica di Church è una rappresentazione formale dei dati e degli operatori all’interno del -calcolo, in cui ogni valore e ogni operazione vengono espressi unicamente attraverso termini.

1 - Logica booleana e strutture di controllo

Partendo dai classici valori booleani e , Church nella sua codifica decise di definirli seguendo una semplice regola: presi due valori in input e (tramite currying), TRUE restituirà il primo valore () e FALSE il secondo ():

Definizione: valori booleani nella codifica di Church

Nella codifica di Church, i valori booleani vero e falso sono rispettivamente codificati nei termini e definiti come segue:

Questa definizione di e può sembrare completamente arbitraria e senza senso così da sola, ma se codifichiamo un termine (che rappresenta una struttura di controllo) come

e applichiamo questo termine nel seguente modo:

allora possiamo dimostrare che verrà correttamente restituito il termine e, se calcoliamo invece il risultato di

otterremo il termine , esattamente come vorremmo che si comportasse una struttura di controllo.

Definizione: struttura di controllo nella codifica di Church

Nella codifica di Church, la struttura di controllo è codificata nel termine definito come segue:

Ora verifichiamo formalmente che l’ si comporta come vorremmo.

Verifica della correttezza semantica dell'

Sia l’insieme di termini del -calcolo.

Dati due termini :

  1. La -espressione è convertibile in :
  1. La -espressione è convertibile in :

Ovviamente, quando andremo a “programmare” realmente tramite il -calcolo, al posto di e ci saranno delle espressioni booleane codificate in -calcolo che, appunto, si ridurranno nei valori e .

Osservazione: usare l' in un linguaggio zelante

Osserviamo cosa succede se proviamo a usare l’ in un linguaggio zelante, ossia usando l’ordine applicativo.

Proviamo per esempio a ridurre la -espressione usando l’ordine applicativo:

Arrivati a questo punto, non possiamo ridurre ulteriormente , ottenendo così un termine che, nel concreto, ci serve poco o niente. Ecco perché si preferisce sempre usare l’ordine normale nell’ambito della codifica di Church.

1.1 - Negazione logica

Avendo codificato i valori booleani e e un che si comporta come ci aspetteremmo, ora possiamo creare condizioni booleane più elaborate provando a codificare anche gli operatori booleani.

Per esempio, una semplice negazione logica potrebbe funzionare così: preso in input un argomento , se () è vero allora restituisce falso (FALSE), altrimenti restituisce vero (TRUE):

Definizione: negazione logica nella codifica di Church

Nella codifica di Church, la negazione logica è codificata nel termine definito come segue:

Verifichiamo che semanticamente il si comporti come ci aspettiamo.

Verifica della correttezza semantica del

  1. La -espressione è convertibile in :
  1. La -espressione è convertibile in :

1.2 - Congiunzione logica

Per la congiunzione logica , presi in input due argomenti e , se () è vero allora restituisce il valore di (perché da dipenderà se l’ è vero o falso), altrimenti (essendo già falso) restituisce il valore falso ():

Definizione: congiunzione logica nella codifica di Church

Nella codifica di Church, la congiunzione logica è codificata nel termine definito come segue:

Verifica della correttezza semantica dell'

  1. La -espressione è convertibile in :
  1. La -espressione è convertibile in :
  1. La -espressione è convertibile in :
  1. La -espressione è convertibile in :

1.3 - Disgiunzione logica

Per la disgiunzione logica , presi in input due argomenti e , se () è vero allora restituisce vero (), altrimenti restituisce il valore di (perché da dipenderà se l’ è vero o falso):

Definizione: disgiunzione logica nella codifica di Church

Nella codifica di Church, la disgiunzione logica è codificata nel termine definito come segue:

Verifica della correttezza semantica dell'

  1. La -espressione è convertibile in :
  1. La -espressione è convertibile in :
  1. La -espressione è convertibile in :
  1. La -espressione è convertibile in :

2 - Coppie di termini

Definizione: coppie nella codifica di Church

Nella codifica di Church, le coppie sono codificate nel termine definito come segue:

Il primo elemento della coppia è codificato nel termine definito come segue:

Il secondo elemento della coppia è codificato nel termine definito come segue:

Verifica della correttezza semantica di PAIR, FIRST e SECOND

Sia l’insieme di termini del -calcolo.

Dati due termini :

  1. La -espressione è convertibile in :
  1. La -espressione è convertibile in :

3 - Numerali di Church

Può sembrare scontato, ma uno strumento importante in ogni linguaggio di programmazione sono i numeri, uno dei tipi di dato più utili in un linguaggio.

Nella sua codifica, Alonzo Church ha deciso di rappresentare ogni numero naturale , detto numerale di Church e rappresentato come , come una funzione che mappa una qualsiasi funzione alla sua composizione per volte:

Prima di andare avanti, definiamo un attimo più formalmente questa notazione “esponenziale” dell’applicazione.

Definizione: applicazione esponenziale

Sia l’insieme di termini del -calcolo. Dati due termini e un , l’applicazione esponenziale di per volte a , denotata con , è definita nel seguente modo:

In particolare:

Ritornando ai numerali, per un numero naturale qualsiasi, data una funzione e un parametro , verrà restituito il parametro su cui viene applicata la funzione per volte:

Possiamo quindi formalmente definire i numerali di Church.

Definizione: numerale di Church

Nella codifica di Church, un numerale di Church è un termine che codifica un numero naturale ed è definito come segue:

Ecco una breve tabella che riassume il comportamento dei numerali di Church:

Numerale di ChurchDefinizione del numeraleUtilizzo del numerale

Osservazione: numerali di Church come codifica unaria dei numeri naturali

I numerali di Church costituiscono una codifica unaria dei numeri naturali, ossia rappresentano un numero n come n applicazioni di una funzione.

La codifica unaria ha il vantaggio della semplicità concettuale e della chiarezza semantica, ma non è efficiente nei casi pratici per numeri grandi, proprio perché ogni numero è “codificato” con un numero di applicazioni proporzionale al valore numerico.

3.1 - Successore naturale

Nell’ambito dei numerali di Church, introduciamo un operatore, , che ci permette di ottenere il successore di un certo numerale (es. ).

Per far ciò, partiamo dalla definizione del numerale di Church (): prendendo in input un numerale (), gli applichiamo un’altra volta ancora :

Definizione: successore naturale nella codifica di Church

Nella codifica di Church, il successore naturale è codificato nel termine definito come segue:

Ora verifichiamo che , per come l’abbiamo definito, si comporti come vorremmo.

Verifica della correttezza semantica di

Dato un numerale di Church , vale:

3.2 - Predecessore naturale

Vogliamo ottenere una funzione PRED tale che:

PRED n=n-1

con PRED 0=0.

Prendendo ispirazione da SUCC, definito come

SUCC\overset{\text{def}}{=}a.f.x.a f (f x)

possiamo notare che, per calcolare il successoreLINK di un numero naturale, basta aggiungergli un’applicazione. Viceversa, per calcolare il suo predecessoreLINK, dovremo togliere un’applicazione.

Tuttavia, nel -calcolo, non è possibile togliere applicazioni a una funzione di Church. Occorre quindi un espediente funzionale.

L’idea di Church è questa: quando applichiamo una funzione f per n volte, vogliamo tener traccia non solo dell’ultimo risultato, ma anche del penultimo. Se riuscissimo a costruire una sequenza di coppie

(0,0),(0,1),(1,2),(2,3),…,(n-2,n-1),(n-1,n)

alla fine potremmo restituire il primo elemento della coppia finale per ottenere n-1.

Perciò, costruiamo una funzione “iteratrice” next(x) che parte da x=(0,0) e ad ogni passo aggiorna la coppia in questo modo:

next(x)=(x1second(x),x2succ(second(x)))

Così facendo, la funzione next(x) funzionerà nel seguente modo:

xx1=second(x)x2=succ(second(x))next(x)=(x1,x2)
(0,0)second((0,0))=0succ(second((0,0)))=succ(0)=1next((0,0))=(0,1)
(0,1)second((0,1))=1succ(second((0,1)))=succ(1)=2next((0,1))=(1,2)
(1,2)second((1,2))=2succ(second((1,2)))=succ(2)=3next((1,2))=(2,3)
(2,3)second((2,3))=3succ(second((2,3)))=succ(3)=4next((2,3))=(3,4)
(n-2,n-1)second((n-2,n-1))=n-1succ(second((n-2,n-1)))=succ(n-1)=nnext((n-2,n-1))=(n-1,n)
(n-1,n)second((n-1,n))=nsucc(second((n-1,n)))=succ(n)=n+1next((n-1,n))=(n,n+1)

Osserviamo che dopo n passi di next(x), la prima componente (x1) è esattamente n-1 (per n>0).

Nel -calcolo traduciamo questa funzione next(x) come:

NEXT\overset{\text{def}}{=}p.PAIR (SECOND p) (SUCC (SECOND p))

La nostra -espressione PRED dovrà quindi prendere la prima componente (FIRST) della coppia generata all’n-esima iterazione di NEXT:

PRED\overset{\text{def}}{=}n.FIRST (n NEXT (PAIR 0 0))

Definizione: predecessore naturale nella codifica di Church

Nella codifica di Church, il predecessore naturale è codificato nel termine PRED definito come segue:

PRED\overset{\text{def}}{=}n.FIRST (n NEXT (PAIR 0 0))

dove

NEXT\overset{\text{def}}{=}p.PAIR (SECOND p) (SUCC (SECOND p))

Verifica della correttezza semantica di PRED

Dato un numerale di Church n:

  1. Il successore della coppia (n-1,n) è (n,n+1):

NEXT (PAIR n-1 n)⇔PAIR n n+1

  1. Il predecessore di 0 è 0 e il predecessore di n è n-1:

PRED n⇔{0n-1n=0n>0

3.3 - Addizione

Definizione: addizione nella codifica di Church

Nella codifica di Church, l’addizione è codificata nel termine ADD definito come segue:

ADD\overset{\text{def}}{=}a.b.b SUCC a

Verifica della correttezza semantica di ADD

Dati due numerali di Church m e n, vale:

ADD m n⇔m+n

3.4 - Moltiplicazione

Definizione: moltiplicazione nella codifica di Church

Nella codifica di Church, la moltiplicazione è codificata nel termine MUL definito come segue:

MUL\overset{\text{def}}{=}a.b.b (ADD a) 0

3.5 - Esponenziazione

Definizione: esponenziazione nella codifica di Church

Nella codifica di Church, l’esponenziazione è codificata nel termine EXP definito come segue:

EXP\overset{\text{def}}{=}a.b.b (MUL a) 1

3.6 - Test per zero

Definizione: test per zero nella codifica di Church

Nella codifica di Church, il test per zero è codificato nel termine ISZERO definito come segue:

ISZERO\overset{\text{def}}{=}a.a (x.FALSE) TRUE

Verifica della correttezza semantica di ISZERO

Dato un numerale di Church n, se n=0 allora ISZERO n restituisce TRUE, altrimenti FALSE:

ISZERO n⇔{TRUEFALSEn=0n=0

4 - Punti fissi e funzioni ricorsive

Ora che abbiamo le operazioni aritmetiche per calcolare con i numerali di Church, possiamo cimentarci nella codifica di una prima funzione ricorsiva nel -calcolo: il fattoriale.

Il fattoriale di un numero naturale n, tradizionalmente, nella sua definizione ricorsiva si esprime come il n moltiplicato per il fattoriale del suo predecessore n-1 (con il fattoriale di 0 uguale a 1):

n!={1n∗(n-1)!n=0n>0

Come primo tentativo, potremmo tradurre questa definizione ricorsiva del fattoriale nella codifica di Church così:

FACT\overset{\text{def}}{=}a.IF (ISZERO a) 1 (MUL a (FACT (PRED a)))

Tuttavia, ciò non ci soddisfa perché il termine FACT compare pure a destra. Proviamo però ad astrarre ulteriormente questo termine passandogli il FACT nella sua definizione come argomento:

FACT\overset{\text{def}}{=}(f.a.IF (ISZERO a) 1 (MUL a (f (PRED a)))) FACT

(Ovviamente possiamo notare che, tramite un semplice passaggio di -riduzione, sostituiamo f con FACT e torniamo alla situazione iniziale.)

Assegniamo momentaneamente al termine “ausiliare” AUX tutto quell’ambaradan a cui va poi applicato ricorsivamente FACT:

AUX\overset{\text{def}}{=}f.a.IF (ISZERO a) 1 (MUL a (f (PRED a)))

In qualche modo, una definizione “accettabile” del fattoriale che vogliamo ottenere avrà una forma del tipo:

FACT\overset{\text{def}}{=}AUX AUX AUX …

Ma a noi serve avere una definizione finita per poter operare con questo termine e ottenere il risultato che vogliamo. È per questo che ci torna utile il concetto di punto fisso di una funzione che ci permetterà di definirla in maniera finita.

Non preoccuparti, a breve sarà tutto più chiaro, ma intanto beccati questa definizione di un punto fisso nel -calcolo espresso mediante questo particolare combinatore scoperto da Haskell Curry: il combinatore di punto fisso.

Definizione: combinatore di punto fisso Y

Il combinatore di punto fisso (anche detto combinatore di Curry perché scoperto da Haskell Curry) Y è un combinatore definito nel seguente modo:

Y\overset{\text{def}}{=}f.(x.f(x x)) (x.f(x x))

Possiamo dimostrare che questo combinatore di punto fisso Y si comporta semanticamente proprio come un punto fisso.

Verifica della correttezza semantica del combinatore di punto fisso Y

Sia l’insieme di termini del -calcolo.

Dato un termine M\in, vale:

Y M=M (Y M)

4.1 - Fattoriale

Il combinatore di punto fisso Y è uno strumento molto potente: ci permette, infatti, di implementare il concetto di ricorsione in un linguaggio, quello del -calcolo, che formalmente non ce l’ha.

Ciò infatti è possibile perché, ogni termine M a cui viene applicato il combinatore di punto fisso Y, diventa a sua volta l’argomento della -espressione di partenza:

Y M=M (Y M)=M (M (Y M))=M (M (M (Y M)))=…

Riprendendo quel termine “ausiliare” di prima, che abbiamo espresso come

AUX\overset{\text{def}}{=}f.a.IF (ISZERO a) 1 (MUL a (f (PRED a)))

possiamo renderci ora conto che, passandogli come argomento al posto della f il combinatore di punto fisso Y, ci aiuta a definire in maniera finita la funzione fattoriale nel -calcolo.

Definizione: fattoriale nella codifica di Church

Nella codifica di Church, il fattoriale è codificato nel termine FACT definito come segue:

FACT\overset{\text{def}}{=}(f.a.IF (ISZERO a) 1 (MUL a (f (PRED a)))) Y

Sì, ma siamo sicuri che quella che abbiamo appena scritto non sia una cazzata colossale e che effettivamente la ricorsione funzioni? Verifichiamolo.

Verifica della ricorsione nel fattoriale nella codifica di Church

Il termine FACT è convertibile nel seguente modo:

FACT⇔a.IF (ISZERO a) 1 (MUL a (FACT (PRED a)))


Fonti