Negli anni Venti e Trenta del Novecento, logici e matematici come Hilbert, Gödel, Church e Turing cercavano un modello rigoroso di “procedura effettiva” (ossia di algoritmo) per affrontare il cosiddetto Entscheidungsproblem (problema della decisione): esiste un metodo meccanico per decidere la verità o falsità di qualunque enunciato matematico?

Per rispondere, occorreva chiarire cosa significhi calcolare in modo puramente meccanico: come descrivere formalmente una sequenza di operazioni che, dato un valore iniziale, produca un risultato.

Il matematico statunitense Alonzo Church sviluppò allora un formalismo minimale che prendeva come unica base il concetto di funzione matematica, in quanto una funzione può essere rappresentata come un processo che, dato un certo input, restituisce un certo output. La sua intuizione fu di considerare le funzioni non più soltanto come relazioni immutabili tra insiemi, ma come oggetti matematici manipolabili, su cui fondare una nuova branca della matematica: ecco quindi la nascita del -calcolo.

Definizione: -calcolo

Il -calcolo è un sistema formale formulato a partire dagli anni Trenta dal matematico americano Alonzo Church, sviluppato per esprimere formalmente il procedimento di computazione di una funzione matematica espressa per mezzo di un linguaggio formale i cui termini possono essere usati per rappresentare calcoli in modo astratto.

Il -calcolo essenzialmente permette di “calcolare” con le funzioni matematiche così come è possibile calcolare normalmente con i numeri: avendo anticipato così il concetto di linguaggio di programmazione prima dell’avvento stesso dei computer (che arriveranno solo negli anni Quaranta), il -calcolo viene considerato uno dei primi linguaggi di programmazione della storia ma utilizzabile solo “sulla carta” (perché, appunto, non esistevano ancora i computer lol).

Dal -calcolo, inoltre, nasce uno dei primi paradigmi di programmazione della storia: quello funzionale, che prende il nome proprio dal concetto delle funzioni matematiche su cui è costruito.

Per intenderci, guarda questa espressione apparentemente senza senso:

Sentiti liber* di crederci o no, ma questa espressione, che chiameremo termine, rappresenta il “codice” in -calcolo per calcolare il fattoriale di un numero naturale qualsiasi.

Ma come fa a essere questo ammasso di lettere casuali un codice? Se sei curios* di saperlo, benvenut* nel magico mondo del -calcolo.

(Attent*! Stai per finire in un rabbit-hole da cui potresti non uscirne più. Io ti ho avvertit*…)

Termini del -calcolo

Prima di arrivare a capire cosa sono gli oggetti principali con cui opera il -calcolo, cioè i termini, vediamo come sono nati.

Astrazioni

Durante il processo di definizione del -calcolo, Alonzo Church decise in primis di modificare la notazione classica della definizione di una funzione:

Il signor Church, per rendere la notazione più scorrevole e, soprattutto, il più astratta possibile (in modo da poterci fare operazioni più comodamente), scelse di evitare di indicare per ogni funzione il suo nome (in questo caso ) e gli insiemi posti in relazione ( e ), valorizzando in particolare proprio la trasformazione che avviene grazie alla funzione ().

La nuova notazione di Church rappresenta le funzioni introducendole con un  (da qui il nome del -calcolo) per indicare l’inizio della definizione di una funzione, seguito dalle due variabili separate da un punto:

Questo modo di definire “astrattamente” una funzione viene detto astrazione.

Definizione: astrazione

Nel -calcolo, un’astrazione (anche detta -astrazione) è una definizione di una funzione anonima, cioè una funzione che non ha un nome ma è identificata direttamente dalla sua regola di trasformazione.

Un’astrazione ha la forma:

dove:

Applicazioni

Con l’astrazione abbiamo capito come possiamo definire una funzione e il suo comportamento, ma concretamente come facciamo a “utilizzarla” dandole in input un certo numero?

Il mitico Alonzo decise di adottare quindi la seguente notazione: presa un’astrazione , questa viene posta tra parentesi tonde e viene seguita dal numero  che vogliamo sostituire all’argomento dell’astrazione:

Per esempio, data un’astrazione  dove  indica la somma di  a  (quindi ) e  è uguale a , potremmo scrivere (seppur effettuando in questo caso un abuso di notazione, perché nel -calcolo non è possibile usare direttamente i numeri in questo modo):

Ciò ci indica che, alla variabile di input  che rappresenta l’argomento dell’astrazione, noi dobbiamo sostituire il numero , in modo da ottenere come risultato : a questa notazione Church diede il nome di applicazione.

Definizione: applicazione

Nel -calcolo, un’applicazione (anche detta -applicazione) è l’operazione che consiste nell’utilizzare una funzione su un argomento.

Un’applicazione ha la forma:

dove:

  •  è un’espressione del -calcolo che rappresenta una funzione e
  •  è un’espressione del -calcolo che rappresenta l’argomento su cui la funzione viene applicata.

Termini

Ora che abbiamo definito cosa sono un’astrazione e un’applicazione, potremmo dire che questi due sono gli oggetti fondamentali con cui andremo a operare nel -calcolo: i nostri termini, quindi, possono assumere una di queste forme sintattiche possibili tra un’astrazione, un’applicazione e una semplice variabile.

Definizione: termine nel -calcolo

Nel -calcolo, un termine  (anche detto -termine o -espressione) è una stringa ben formata a partire dalla seguente grammatica espressa in BNF:

dove:

  1.  è una variabile,
  2.  è un’astrazione con argomento  e corpo  e
  3.  è un’applicazione in cui la funzione  viene applicata all’argomento .

Ognuna di queste 3 forme che può assumere un termine è detta forma sintattica.

Esempi di termini

Ecco qualche esempio di termini del -calcolo correttamente espressi:

  •  è un termine che rappresenta una semplice variabile .
  •  è un termine che rappresenta un’astrazione con argomento  e corpo .
  •  è un termine che rappresenta un’applicazione della funzione  all’argomento . A loro volta, possiamo analizzare questi “sotto-termini”:
  •  è un termine che rappresenta un’astrazione con argomento  e corpo . A sua volta:
    •  è un termine che rappresenta un’astrazione con argomento  e corpo . A sua volta:

Riscrittura dei termini

È possibile riscrivere i termini eliminando alcune parentesi per migliorare la leggibilità.

Notazione: omissione delle parentesi più esterne di un termine

In un termine è possibile omettere le parentesi più esterne.

Per esempio, un termine  che rappresenta un’astrazione con argomento  e corpo  si può riscrivere come .

Allo stesso modo, un termine  che rappresenta un’applicazione della funzione  all’argomento  si può riscrivere come .

Notazione: omissione delle parentesi in un'astrazione

In un termine  che rappresenta un’astrazione con argomento  e corpo  si possono eliminare le parentesi più esterne del sotto-termine .

Per esempio, un termine  si può riscrivere come . Semplicemente, se non sono presenti delle parentesi, tutto quello a destra del punto viene considerato come il corpo dell’astrazione (che in questo caso corrisponderebbe a ).

Notazione: omissione delle parentesi in un'applicazione

In un termine  che rappresenta un’applicazione della funzione  all’argomento  si possono eliminare le parentesi più esterne della funzione .

Per esempio, un termine  si può riscrivere come , ma NON come ​ perché  è l’argomento dell’applicazione.

Esercizio 1 sulla riscrittura dei termini

Rimuovere il più possibile le parentesi, senza cambiare il significato, del seguente termine:

Soluzione

È possibile innanzitutto rimuovere le parentesi più esterne del termine:

A questo punto, avendo un’astrazione con argomento  e corpo , è possibile rimuovere le parentesi nel corpo:

Il corpo  x è un’applicazione della funzione  all’argomento , quindi possiamo rimuovere le parentesi della funzione:

Variabili libere e legate in un termine

Prima di cominciare a effettuare operazioni con i termini, ci serve definire un ultimo concetto che ci permette di capire quale valore hanno le variabili all’interno dei termini stessi: per esempio, nell’astrazione  e nell’applicazione  la variabile  non ha lo stesso “peso”, perché nel primo caso è “legata” al termine  (essendo un argomento di quella funzione), mentre nel secondo caso è “libera” e non ha alcun particolare legame con .

Fare questa distinzione è importante perché, svolgendo operazioni sui termini, dobbiamo assicurarci che il loro significato non venga modificato andando a toccare quelle variabili “legate”.

Definiamo formalmente quindi questa differenza.

Definizione: variabili libere e legate

L’insieme delle variabili libere di un termine , denotate con  (dall’inglese free variables), è definito induttivamente sulla struttura di  come segue:

  • Quando il termine  è una variabile :

  • Quando il termine  è un’astrazione :

  • Quando il termine  è un’applicazione :

Una variabile contenuta in  si dice libera se è presente in legata altrimenti.

Esempi di variabili libere e legate

Ecco qualche esempio di variabili libere e legate in diversi termini:

  • : la variabile  è libera perché non è vincolata da alcuna astrazione:

  • : la variabile  è legata perché è vincolata dall’astrazione (essendo un suo argomento):

  • : la variabile  è legata perché è vincolata dall’astrazione (essendo un suo argomento), ma la variabile  no:

  • : le variabili  e  sono legate perché vincolate dalle rispettive astrazioni (essendo loro argomenti), ma la variabile  no:

Osservazione: variabili libere e legate come visibilità nei linguaggi di programmazione

La distinzione tra variabili libere e legate è utile a farci capire, trasportando questo concetto sui linguaggi di programmazione, a capire qual è la visibilità delle variabili che usiamo:

  • Le variabili legate possono essere pensate come variabili “locali” della funzione, utilizzabili unicamente all’interno di quella funzione e senza bisogno di altre informazioni dall’esterno per capire a cosa servono.
  • Le variabili libere possono essere pensate come variabili “globali” della funzione, usate al suo interno ma in realtà dichiarate all’esterno della funzione e, quindi, il loro valore dipende dal contesto in cui si trova la funzione.

Definizione: cattura di una variabile libera

Una cattura è l’operazione con cui una variabile libere diventa legata.

Combinatori

Un concetto strettamente collegato a quello di variabili libere e che ci tornerà utile più tardi è quello dei combinatori.

Definizione: combinatore

Un combinatore (o termine chiuso) è un termine  senza variabili libere:

Relazioni tra termini

Sostituzione

Come abbiamo visto, un’applicazione  rappresenta essenzialmente la sostituzione delle variabili contenute in  con i valori di . Ma ciò non è un’operazione banale: le uniche variabili sostituibili sono quelle libere e questa è una cosa di cui bisogna tener conto per evitare catture. Definiamo quindi correttamente come deve funzionare una sostituzione in un termine.

Definizione: sostituzione

Nel -calcolo, una sostituzione è un’operazione binaria tra due termini  ed  in cui una variabile libera  di  viene sostituita con . Viene denotata come  ed è definita induttivamente sulla struttura di  come segue:

  • Quando il termine  è una variabile :

  • Quando il termine  è un’astrazione :

    con 

  • Quando il termine  è un’applicazione ​:

-conversione

Abbiamo detto che le funzioni matematiche sono quindi un modo per rappresentare in maniera generica una relazione tra due insiemi.

Per esempio, la relazione che associa a ogni numero naturale  il suo quadrato  (anch’esso nell’insieme dei numeri naturali ) possiamo rappresentarla con la funzione

Ma al posto di  potremmo usare un’altra variabile per descrivere la funzione mantenendo invariato il suo significato, per esempio :

Oppure anche un simbolo che non è una lettera, come il simbolo  (si legge star, ossia stella):

Queste tre rappresentazioni sono perfettamente equivalenti e, nell’ambito del -calcolo, Alonzo Church decise di chiamare il processo di rinominare le variabili di una funzione con il nome di -conversione.

Definizione: -conversione

L’-conversione, denotata con ​, è una relazione binaria tra due astrazioni  e  che permette di rinominare la variabile legata  in un’altra variabile legata  senza alterare il significato del termine:

Le due astrazioni  e  si dicono -equivalenti.

-riduzione

In matematica abbiamo che, data per esempio una funzione , allora .

Nell’ambito del -calcoloapplicare un’astrazione  a un argomento  significa valutare il corpo della funzione  in cui ogni occorrenza della variabile libera  è stata sostituita da . Per intenderci, nell’esempio di prima abbiamo valutato applicandola all’argomento .

Questa idea è alla base della -riduzione.

Definizione: -riduzione

La -riduzione, denotata con ​, è l’operazione che permette di sostituire in un’applicazione  l’argomento  dell’astrazione con l’argomento :

In particolare, diciamo che:

  •  è un -redex (da reducible expression, in italiano espressione riducibile) o, in alcuni casi, ricalcato in italiano come redesso e
  •  è il suo ridotto.

L’operazione inversa, cioè quella che dal ridotto ci fa risalire al redesso, viene detta -espansione e viene denotata con ​:

Esempi di -riduzione

Ecco qualche esempio di -riduzione correttamente svolta:

-riduzione

Prendiamo un esempio di -riduzione:

Dal momento che l’applicazione della funzione  all’argomento  e l’applicazione della funzione  all’argomento  generano in entrambi i casi , per il principio di estensionalità delle funzioni potremmo dire che  e  sono due funzioni equivalenti, quindi l’una deve essere trasformabile nell’altra attraverso una -riduzione.

Tuttavia, però, non in tutti i casi può valere la -riduzione che da  ci porta a  o viceversa (attenzione: per poter applicare la -riduzione e ottenere  ci serve avere  che non è equivalente a !):

Ciò ci fa pensare che, per continuare a far valere correttamente il principio di estensionalità delle funzioni, la -riduzione da sola non ci basta (come, per esempio, in questi casi appena visti): abbiamo bisogno di introdurre quindi una nuova operazione, la -riduzione.

Definizione: -riduzione

L’-riduzione, denotata con ​, è l’operazione che permette di ridurre un’astrazione  nel termine :

In particolare, diciamo che:

  •  è un -redex (da reducible expression, in italiano espressione riducibile) o, in alcuni casi, ricalcato in italiano come redesso e
  •  è il suo ridotto.

L’operazione inversa, cioè quella che dal ridotto ci fa risalire al redesso, viene detta -espansione e viene denotata con :

Riduzione singola e multipla

Durante lo svolgimento di -riduzioni ed -riduzioni è comodo alcune volte “generalizzare” il concetto di riduzione senza specificare quale delle due si sta applicando: per questo motivo, chiamiamo riduzione singola, denotata con →, l’uso generale di una delle due riduzioni tra la -riduzione e l’-riduzione.

Definizione: riduzione singola

Una riduzione singola (o, più semplicemente, riduzione), denotata con , è un singolo passo di -riduzione o di -riduzione.

si dice riducibile in .

Avendo definito formalmente la riduzione singola, possiamo ora generalizzare ulteriormente più passi di riduzione in una sola operazione, senza avere la necessità di esplicitarli tutti: definiamo quindi la riduzione multipla.

Definizione: riduzione multipla

Una riduzione multipla, denotata con  (o, in alcuni casi, con ), è una chiusura riflessiva e transitiva della relazione di riduzione singola, ossia la più piccola relazione tale che:

  • Riflessività (zero passi sono ammessi): .
  • Un passo è ammesso: .
  • Transitività (più passi sono ammessi): .

si dice riducibile in zero o più passi in .

Convertibilità

La riduzione multipla è una relazione tra due termini che può essere interpretata come una sorta di “equivalenza” tra di essi: se abbiamo che , allora possiamo dedurre che in qualche modo dal termine  si può arrivare al termine , seppur con più di qualche passaggio di riduzione singola. Viceversa, facendo lo stesso discorso per le espansioni, cioè -espansione ed -espansione, si potrebbe dire che da  possiamo risalire a . Insomma, si potrebbe dire che  ed  sono uguali semanticamente, cioè rappresentano la stessa funzione.

A questa intercambiabilità tra termini collegati da una riduzione multipla diamo il nome di convertibilità.

Definizione: convertibilità

Sia  l’insieme di termini del -calcolo. La relazione binaria  è detta convertibilità ed è definita come segue:

Forma normale e strategie di riduzione

Osservazione: -riduzioni che non terminano

Consideriamo il seguente esempio di -riduzione:

Come possiamo notare, ogni passo di -riduzione non ci permette di ridurre ulteriormente il termine ma ci fa ritornare puntualmente allo stato in cui è nella forma : da ciò possiamo evincere che la -riduzione in alcuni casi può anche non terminare mai.

Questo particolare termine è un combinatore (perché ha solo variabili legate) e scopriremo più tardi che è un combinatore molto famoso, ossia il combinatore di punto fisso .

Intanto, ai termini che prima o poi arrivano a un certo punto in cui non possono essere più ridotti diciamo che sono in forma normale.

Definizione: forma normale

Nel -calcolo, un termine  si dice che è in forma normale e si denota con se non può più essere ridotto, ovvero se non esiste un altro termine  tale che :

dove  è l’insieme di termini del -calcolo.

Confluenza

Teorema della confluenza

Sia  l’insieme di termini del -calcolo. Dati tre termini , con ​, allora esiste un termine  tale che  e .

Corollario del teorema della confluenza

La forma normale di un termine , se esiste, è unica (a meno di -conversioni).

In termini matematici, per ogni termine , se esistono due termini  ed ​ in forma normale ( e ) nei quali  può essere ridotto ( e ​), allora  ed ​ sono -equivalenti (​):

dove  è l’insieme di termini del -calcolo.

Strategie di riduzione

Definizione: ordine applicativo

L’ordine applicativo è una strategia di riduzione in cui applicare una funzione a un argomento significa prima valutare l’argomento e poi sostituire il valore ottenuto nel corpo della funzione. In altre parole, in un termine del tipo

viene scelto il -redex più a sinistra e più interno, ossia in questo caso :

I linguaggi funzionali che utilizzano l’ordine applicativo sono detti linguaggi zelanti (in inglese eager languages).

Definizione: ordine normale

L’ordine normale è una strategia di riduzione in cui applicare una funzione a un argomento significa sostituire l’argomento nel corpo della funzione. In altre parole, in un termine del tipo

viene scelto il -redex più a sinistra e più esterno, ossia in questo caso l’intero termine:

I linguaggi funzionali che utilizzano l’ordine applicativo sono detti linguaggi pigri (in inglese lazy languages).

Osservazione: cosa scegliere tra ordine applicativo e ordine applicativo

Le due strategie di riduzione viste, ossia l’ordine applicativo e l’ordine normalenon sono equivalenti, e spesso conviene usare una al posto dell’altra.

Prendiamo come esempio il seguente termine:

Se eseguito con l’ordine applicativo, otteniamo le seguenti -riduzioni:

Se invece viene eseguito con l’ordine normale, otteniamo la seguente -riduzione:

Notiamo quindi che in questo caso l’ordine normale conviene, perché in questo modo l’argomento  non viene proprio valutato.

Al contrario, consideriamo il seguente termine:

Se eseguito con l’ordine applicativo, otteniamo le seguenti -riduzioni:

Se invece viene eseguito con l’ordine normale, otteniamo le seguenti -riduzioni:

Possiamo notare come invece, in questo caso, convenga usare l’ordine applicativo perché l’argomento  viene usato due volte e in quest’ultimo caso viene valutato entrambe le volte in due step diversi.

Normalizzazione

Teorema della normalizzazione

Dati due termini , se  è convertibile in  (cioè ) ed  è in forma normale (), allora esiste una riduzione multipla composta da -riduzioni in ordine normale (che indichiamo con ) che porta da  a :

Osservazione: teorema della normalizzazione non vale per l'ordine applicativo

Il teorema della normalizzazione vale solo se le -riduzioni vengono eseguite secondo l’ordine normale, mentre non vale se si usa l’ordine applicativo. Un esempio di ciò è dato dal seguente termine:

Se eseguito con l’ordine applicativo, abbiamo:

Possiamo notare che il termine, con questa strategia di riduzione, non raggiunge mai la sua forma normale. Al contrario, con l’ordine normale abbiamo:

Con l’ordine normale raggiungiamo subito la forma normale.

Currying

Abbiamo notato che nelle astrazioni possiamo passare come parametri una singola variabile, il che sembra abbastanza limitante se abbiamo bisogno di usare funzioni che necessitano più parametri.

Possiamo tuttavia usare un piccolo trucchetto sfruttando le funzioni di ordine superiore. Prendendo per esempio due variabili  e  che devono essere passate come parametri alla stessa funzione , possiamo creare un’astrazione che ha come parametro  e nel suo corpo prende un’altra astrazione che ha come parametro  e come corpo :

In questo modo, applicando a quest’astrazione i due valori per i parametri  e  (per esempio, passiamo rispettivamente i valori  e ), tramite la -riduzione possiamo verificare che otterremo proprio quel che vogliamo:

Questo metodo di passare molteplici parametri alla stessa astrazione venne chiamato currying, in onore del matematico Haskell Curry per il suo apporto al -calcolo.

Definizione: currying

Il currying è una tecnica che permette di applicare a un’astrazione con corpo  più valori  per ognuno dei suoi argomenti :


Fonti