Stefano M. Nicoletti

Lezione 3: Dalla deduzione naturale alle dimostrazioni in Lean

Registrazione della lezione

Registrazione video della Lezione 3

1. Scopo della lezione

Nelle lezioni precedenti abbiamo imparato a leggere un proof state e abbiamo usato tattiche come intro, apply, have, exact e cases. In questa lezione colleghiamo quei comandi alle regole della deduzione naturale. Vedremo come le tattiche non siano mosse arbitrarie: costruiscono e usano dimostrazioni secondo la struttura logica delle proposizioni.

Il percorso della lezione è il seguente:

2. Tipi e abitanti

Un tipo descrive una classe di possibili oggetti. Dire che un tipo è abitato significa dire che esiste almeno un termine che ha quel tipo. Se t ha tipo T, scriviamo:

t : T

Possiamo leggere questa espressione come «t è un termine di tipo T» oppure «t abita T». L'idea non riguarda soltanto Lean. Per esempio, il tipo informale «luna di Giove» è abitato perché Io è una luna di Giove. Io è un oggetto concreto che soddisfa la descrizione espressa dal tipo NASA Science, Io: Facts.

In Lean possiamo osservare la stessa struttura con oggetti più semplici:

#check 4       -- 4 : Nat
#check True    -- True : Prop
#check True.intro -- True.intro : True

Nat è abitato, per esempio, dal termine 4. Anche True è abitato: il termine True.intro ha esattamente tipo True. Questa seconda osservazione ci porta dalle relazioni generiche tra termini e tipi alle relazioni tra dimostrazioni e proposizioni.

3. La corrispondenza di Curry-Howard

Secondo la corrispondenza di Curry-Howard, proposizioni e tipi possono essere letti come due descrizioni della stessa struttura. Una proposizione indica che cosa deve essere dimostrato, mentre il tipo corrispondente indica quale genere di termine deve essere costruito. Una dimostrazione della proposizione è un termine che abita quel tipo Wadler, Propositions as Types.

Ad esempio:

LogicaTeoria dei tipi
proposizionetipo
dimostrazionetermine
A → Bfunzione da dimostrazioni di A a dimostrazioni di B
introduzione di costruzione di una funzione
eliminazione di applicazione di una funzione

Perciò verificare una dimostrazione significa controllare che il termine costruito abbia il tipo dichiarato. Una derivazione non si limita a certificare in modo astratto che una proposizione è vera: specifica come costruirne una dimostrazione Theorem Proving in Lean 4, Propositions and Proofs.

4. Deduzione naturale e proof state

La deduzione naturale presenta una dimostrazione come una sequenza di passi locali governati da regole. Ogni regola mostra come ottenere una conclusione a partire da alcune premesse. Le regole di introduzione spiegano come costruire una dimostrazione il cui connettivo principale è quello considerato; le regole di eliminazione spiegano come usare una dimostrazione composta già disponibile Logic and Proof, Natural Deduction for Propositional Logic.

Un giudizio ha la forma Γ ⊢ A.

Γ è il contesto delle assunzioni disponibili e A è la conclusione che vogliamo derivare. Il proof state di Lean riproduce questa organizzazione: il contesto compare sopra il simbolo ⊢, mentre il goal compare dopo di esso.

Quando una regola introduce un'assunzione temporanea, questa viene aggiunta al contesto soltanto nel ramo in cui serve. Per esempio, per dimostrare A → B possiamo assumere temporaneamente A e costruire B. Quando la dimostrazione dell'implicazione è completa, l'assunzione è stata incorporata nella funzione che trasforma dimostrazioni di A in dimostrazioni di B.

5. Implicazione

Introduzione dell'implicazione, →I

Introduzione dell'implicazione, →I

Per dimostrare A → B, introduciamo un'assunzione di tipo A e costruiamo una dimostrazione di B. In Lean, intro esegue precisamente questo passaggio.

Vogliamo dimostrare che, se piove e fa freddo, allora piove. Introduciamo l'assunzione che piova e faccia freddo; abbiamo che piove prendendo la parte sinistra dell'assunzione; usiamo esattamente questo fatto.

-- Ogni esempio segue lo stesso percorso: -- regola di deduzione naturale, lettura in linguaggio naturale, istanza concreta, -- costruzione del termine richiesto nel proof state. -- Introduzione dell'implicazione, →I. -- Per dimostrare `A → B`, introduciamo l'assunzione temporanea `A` e costruiamo `B`. -- Esempio: se piove e fa freddo, allora piove. theorem lecture03_imp_intro (Piove FaFreddo : Prop) : Piove FaFreddo Piove := Piove:PropFaFreddo:PropPiove FaFreddo Piove Piove:PropFaFreddo:ProphPioveEFaFreddo:Piove FaFreddoPiove Piove:PropFaFreddo:ProphPioveEFaFreddo:Piove FaFreddohPiove:PiovePiove All goals completed! 🐙

Eliminazione dell'implicazione, →E

Eliminazione dell'implicazione, →E

Se abbiamo A → B e abbiamo A, possiamo applicare la prima dimostrazione alla seconda e ottenere B. Questa è di nuovo la regola del modus ponens.

Vogliamo dimostrare che prendo l'ombrello, date le assunzioni che, se piove, prendo l'ombrello e che piove. Applichiamo la prima assunzione alla seconda; abbiamo che prendo l'ombrello e usiamo esattamente questo fatto.

-- Eliminazione dell'implicazione, →E: modus ponens. -- Da `A → B` e `A` otteniamo `B`. -- Esempio: se piove, prendo l'ombrello; piove; dunque prendo l'ombrello. theorem lecture03_imp_elim (Piove PrendoOmbrello : Prop) (hPioveOmbrello : Piove PrendoOmbrello) (hPiove : Piove) : PrendoOmbrello := Piove:PropPrendoOmbrello:ProphPioveOmbrello:Piove PrendoOmbrellohPiove:PiovePrendoOmbrello Piove:PropPrendoOmbrello:ProphPioveOmbrello:Piove PrendoOmbrellohPiove:PiovehPrendoOmbrello:PrendoOmbrelloPrendoOmbrello All goals completed! 🐙

Qui hPioveOmbrello si comporta come una funzione: riceve il termine hPiove di tipo Piove e restituisce un termine di tipo PrendoOmbrello.

6. Congiunzione

Introduzione della congiunzione, ∧I

Introduzione della congiunzione, ∧I

Per dimostrare A ∧ B dobbiamo costruire entrambe le componenti. Applicando And.intro, il goal viene diviso in due casi: prima A, poi B.

-- Introduzione della congiunzione, ∧I. -- Da una dimostrazione di A e una di B costruiamo una dimostrazione di A ∧ B. -- Esempio: piove; fa freddo; dunque piove e fa freddo. theorem lecture03_and_intro (Piove FaFreddo : Prop) (hPiove : Piove) (hFaFreddo : FaFreddo) : Piove FaFreddo := Piove:PropFaFreddo:ProphPiove:PiovehFaFreddo:FaFreddoPiove FaFreddo Piove:PropFaFreddo:ProphPiove:PiovehFaFreddo:FaFreddoPiovePiove:PropFaFreddo:ProphPiove:PiovehFaFreddo:FaFreddoFaFreddo Piove:PropFaFreddo:ProphPiove:PiovehFaFreddo:FaFreddoPiove All goals completed! 🐙 Piove:PropFaFreddo:ProphPiove:PiovehFaFreddo:FaFreddoFaFreddo All goals completed! 🐙

I due punti elenco (·) appartengono a due goal distinti. Una dimostrazione della congiunzione è completa soltanto quando entrambi sono chiusi (simboleggiano i due congiunti).

Eliminazione della congiunzione, ∧Eₗ e ∧Eᵣ

Eliminazione sinistra della congiunzione, ∧Eₗ
Eliminazione destra della congiunzione, ∧Eᵣ

Una dimostrazione di A ∧ B contiene una dimostrazione di A e una di B. Possiamo selezionare la componente sinistra con .left e quella destra con .right.

-- Eliminazione sinistra della congiunzione, ∧Eₗ. -- Da `A ∧ B` selezioniamo la componente A. -- Esempio: piove e fa freddo; dunque piove. theorem lecture03_and_elim_left (Piove FaFreddo : Prop) (hPioveEFaFreddo : Piove FaFreddo) : Piove := Piove:PropFaFreddo:ProphPioveEFaFreddo:Piove FaFreddoPiove Piove:PropFaFreddo:ProphPioveEFaFreddo:Piove FaFreddohPiove:PiovePiove All goals completed! 🐙
-- Eliminazione destra della congiunzione, ∧Eᵣ. -- Da `A ∧ B` selezioniamo la componente B. -- Esempio: piove e fa freddo; dunque fa freddo. theorem lecture03_and_elim_right (Piove FaFreddo : Prop) (hPioveEFaFreddo : Piove FaFreddo) : FaFreddo := Piove:PropFaFreddo:ProphPioveEFaFreddo:Piove FaFreddoFaFreddo Piove:PropFaFreddo:ProphPioveEFaFreddo:Piove FaFreddohFaFreddo:FaFreddoFaFreddo All goals completed! 🐙

L'introduzione costruisce una congiunzione a partire dalle componenti; l'eliminazione percorre il cammino opposto e recupera una componente dalla congiunzione disponibile.

7. Disgiunzione

Introduzione della disgiunzione, ∨Iₗ e ∨Iᵣ

Introduzione sinistra della disgiunzione, ∨Iₗ
Introduzione destra della disgiunzione, ∨Iᵣ

Per dimostrare A ∨ B è sufficiente costruire uno dei due lati, ma dobbiamo specificare quale. Or.inl sceglie il lato sinistro; Or.inr sceglie il lato destro.

-- Introduzione sinistra della disgiunzione, ∨Iₗ. -- Da A costruiamo A ∨ B scegliendo il lato sinistro. -- Esempio: piove; dunque piove oppure nevica. theorem lecture03_or_intro_left (Piove Nevica : Prop) (hPiove : Piove) : Piove Nevica := Piove:PropNevica:ProphPiove:PiovePiove Nevica Piove:PropNevica:ProphPiove:PiovePiove All goals completed! 🐙
-- Introduzione destra della disgiunzione, ∨Iᵣ. -- Da B costruiamo A ∨ B scegliendo il lato destro. -- Esempio: nevica; dunque piove oppure nevica. theorem lecture03_or_intro_right (Piove Nevica : Prop) (hNevica : Nevica) : Piove Nevica := Piove:PropNevica:ProphNevica:NevicaPiove Nevica Piove:PropNevica:ProphNevica:NevicaNevica All goals completed! 🐙

Eliminazione della disgiunzione, ∨E

Eliminazione della disgiunzione, ∨E

Se abbiamo A ∨ B, non sappiamo in generale quale lato sia stato dimostrato. Per ottenere una stessa conclusione C, dobbiamo quindi dimostrarla sia nel caso A sia nel caso B.

Vogliamo dimostrare che prendo l'ombrello, date le assunzioni che piove oppure nevica, che se piove prendo l'ombrello e che se nevica prendo l'ombrello. Procediamo per casi sulla disgiunzione. In ogni ramo compare soltanto l'assunzione corrispondente a quel caso.

-- Eliminazione della disgiunzione, ∨E. -- Da `A ∨ B`, `A → C` e `B → C` otteniamo C ragionando per casi. -- Esempio: piove oppure nevica; in entrambi i casi prendo l'ombrello; -- dunque prendo l'ombrello. theorem lecture03_or_elim (Piove Nevica PrendoOmbrello : Prop) (hPioveONevica : Piove Nevica) (hPioveOmbrello : Piove PrendoOmbrello) (hNevicaOmbrello : Nevica PrendoOmbrello) : PrendoOmbrello := Piove:PropNevica:PropPrendoOmbrello:ProphPioveONevica:Piove NevicahPioveOmbrello:Piove PrendoOmbrellohNevicaOmbrello:Nevica PrendoOmbrelloPrendoOmbrello cases hPioveONevica with Piove:PropNevica:PropPrendoOmbrello:ProphPioveOmbrello:Piove PrendoOmbrellohNevicaOmbrello:Nevica PrendoOmbrellohPiove:PiovePrendoOmbrello Piove:PropNevica:PropPrendoOmbrello:ProphPioveOmbrello:Piove PrendoOmbrellohNevicaOmbrello:Nevica PrendoOmbrellohPiove:PiovehPrendoOmbrello:PrendoOmbrelloPrendoOmbrello All goals completed! 🐙 Piove:PropNevica:PropPrendoOmbrello:ProphPioveOmbrello:Piove PrendoOmbrellohNevicaOmbrello:Nevica PrendoOmbrellohNevica:NevicaPrendoOmbrello Piove:PropNevica:PropPrendoOmbrello:ProphPioveOmbrello:Piove PrendoOmbrellohNevicaOmbrello:Nevica PrendoOmbrellohNevica:NevicahPrendoOmbrello:PrendoOmbrelloPrendoOmbrello All goals completed! 🐙

cases elimina la disgiunzione rendendo esplicite le varie possibilità tramite una dimostrazione per casi.

8. Negazione, verità e falsità

Negazione

In Lean, ¬A è definita come A → False. Una dimostrazione di ¬A è quindi una funzione che trasforma ogni ipotetica dimostrazione di A in una contraddizione.

Introduzione della negazione, ¬I

Per introdurre ¬A, introduciamo l'assunzione A e costruiamo False. Vogliamo dimostrare che non ho bevuto caffè, date le assunzioni che, se bevo caffè, resto sveglio e che non resto sveglio.

-- Introduzione della negazione, ¬I. -- Per dimostrare `¬A`, introduciamo l'assunzione A e deriviamo False. -- Esempio: se bevo caffè resto sveglio, ma non resto sveglio; dunque non ho -- bevuto caffè. theorem lecture03_not_intro (BevoCaffe RestoSveglio : Prop) (hCaffeSveglio : BevoCaffe RestoSveglio) (hNonSveglio : ¬RestoSveglio) : ¬BevoCaffe := BevoCaffe:PropRestoSveglio:ProphCaffeSveglio:BevoCaffe RestoSvegliohNonSveglio:¬RestoSveglio¬BevoCaffe BevoCaffe:PropRestoSveglio:ProphCaffeSveglio:BevoCaffe RestoSvegliohNonSveglio:¬RestoSvegliohBevoCaffe:BevoCaffeFalse BevoCaffe:PropRestoSveglio:ProphCaffeSveglio:BevoCaffe RestoSvegliohNonSveglio:¬RestoSvegliohBevoCaffe:BevoCaffehRestoSveglio:RestoSveglioFalse BevoCaffe:PropRestoSveglio:ProphCaffeSveglio:BevoCaffe RestoSvegliohNonSveglio:¬RestoSvegliohBevoCaffe:BevoCaffehRestoSveglio:RestoSvegliohContraddizione:FalseFalse All goals completed! 🐙
Eliminazione della negazione, ¬E

Per eliminare una negazione, applichiamo ¬A ad A e otteniamo False.

-- Eliminazione della negazione, ¬E. -- Da ¬A e A otteniamo False. -- Esempio: il laboratorio è aperto e non è aperto; le assunzioni sono contraddittorie. theorem lecture03_not_elim (LaboratorioAperto : Prop) (hLaboratorioAperto : LaboratorioAperto) (hLaboratorioNonAperto : ¬LaboratorioAperto) : False := LaboratorioAperto:ProphLaboratorioAperto:LaboratorioApertohLaboratorioNonAperto:¬LaboratorioApertoFalse LaboratorioAperto:ProphLaboratorioAperto:LaboratorioApertohLaboratorioNonAperto:¬LaboratorioApertohContraddizione:FalseFalse All goals completed! 🐙

Verità e falsità

Introduzione della verità, ⊤I

True ha un costruttore canonico e non richiede assunzioni:

-- Introduzione di True, ⊤I. -- True ha un costruttore canonico e non richiede premesse. theorem lecture03_true_intro : True := True All goals completed! 🐙 -- Oppure: trivial

exact True.intro può anche essere sostituito con la tattica trivial.

Eliminazione della falsità, ⊥E

False, invece, non ha costruttori. Se il contesto contiene già una dimostrazione di False, possiamo eliminare la falsità e ottenere qualunque proposizione. Questo principio è chiamato anche ex falso quodlibet, dal falso segue qualsiasi proposizione: apply False.elim può anche essere sostituito con la tattica exfalso, infatti.

-- Eliminazione di False, ⊥E. -- Da una contraddizione possiamo ottenere qualunque proposizione. -- Esempio: da assunzioni contraddittorie concludiamo che piove. theorem lecture03_false_elim (Piove : Prop) (hContraddizione : False) : Piove := Piove:ProphContraddizione:FalsePiove Piove:ProphContraddizione:FalseFalse -- Oppure: exfalso All goals completed! 🐙

Questa regola non dice che possiamo dimostrare arbitrariamente qualsiasi cosa: possiamo farlo se abbiamo già costruito una contraddizione.

9. Bicondizionale

Una dimostrazione di A ↔ B contiene due funzioni: una da A a B e una da B ad A.

Introduzione del bicondizionale, ↔I

Introduzione del bicondizionale, ↔I

Per dimostrare che «piove e fa freddo» equivale a «fa freddo e piove», applichiamo Iff.intro. Nel primo caso costruiamo la direzione da sinistra a destra; nel secondo costruiamo la direzione inversa.

-- Introduzione del bicondizionale, ↔I. -- Costruiamo entrambe le direzioni e le uniamo. -- Esempio: `Piove ∧ FaFreddo` equivale a `FaFreddo ∧ Piove`. theorem lecture03_iff_intro (Piove FaFreddo : Prop) : Piove FaFreddo FaFreddo Piove := Piove:PropFaFreddo:PropPiove FaFreddo FaFreddo Piove Piove:PropFaFreddo:PropPiove FaFreddo FaFreddo PiovePiove:PropFaFreddo:PropFaFreddo Piove Piove FaFreddo Piove:PropFaFreddo:PropPiove FaFreddo FaFreddo Piove Piove:PropFaFreddo:ProphPioveEFaFreddo:Piove FaFreddoFaFreddo Piove Piove:PropFaFreddo:ProphPioveEFaFreddo:Piove FaFreddoFaFreddoPiove:PropFaFreddo:ProphPioveEFaFreddo:Piove FaFreddoPiove Piove:PropFaFreddo:ProphPioveEFaFreddo:Piove FaFreddoFaFreddo All goals completed! 🐙 Piove:PropFaFreddo:ProphPioveEFaFreddo:Piove FaFreddoPiove All goals completed! 🐙 Piove:PropFaFreddo:PropFaFreddo Piove Piove FaFreddo Piove:PropFaFreddo:ProphFaFreddoEPiove:FaFreddo PiovePiove FaFreddo Piove:PropFaFreddo:ProphFaFreddoEPiove:FaFreddo PiovePiovePiove:PropFaFreddo:ProphFaFreddoEPiove:FaFreddo PioveFaFreddo Piove:PropFaFreddo:ProphFaFreddoEPiove:FaFreddo PiovePiove All goals completed! 🐙 Piove:PropFaFreddo:ProphFaFreddoEPiove:FaFreddo PioveFaFreddo All goals completed! 🐙

Eliminazione del bicondizionale, ↔Eₗ e ↔Eᵣ

Eliminazione sinistra-destra del bicondizionale, ↔Eₗ
Eliminazione destra-sinistra del bicondizionale, ↔Eᵣ

Iff.mp estrae la direzione sinistra-destra ( la direzione del modus ponens), mentre Iff.mpr estrae la direzione destra-sinistra (modus ponens reversed).

-- Eliminazione sinistra-destra del bicondizionale, ↔Eₗ. -- Estraiamo A → B e la applichiamo ad A. theorem lecture03_iff_elim_left (NumeroPari DivisibilePerDue : Prop) (hEquivalenza : NumeroPari DivisibilePerDue) (hNumeroPari : NumeroPari) : DivisibilePerDue := NumeroPari:PropDivisibilePerDue:ProphEquivalenza:NumeroPari DivisibilePerDuehNumeroPari:NumeroPariDivisibilePerDue NumeroPari:PropDivisibilePerDue:ProphEquivalenza:NumeroPari DivisibilePerDuehNumeroPari:NumeroParihDirezione:NumeroPari DivisibilePerDueDivisibilePerDue NumeroPari:PropDivisibilePerDue:ProphEquivalenza:NumeroPari DivisibilePerDuehNumeroPari:NumeroParihDirezione:NumeroPari DivisibilePerDuehDivisibilePerDue:DivisibilePerDueDivisibilePerDue All goals completed! 🐙
-- Eliminazione destra-sinistra del bicondizionale, ↔Eᵣ. -- Estraiamo B → A e la applichiamo a B. theorem lecture03_iff_elim_right (NumeroPari DivisibilePerDue : Prop) (hEquivalenza : NumeroPari DivisibilePerDue) (hDivisibilePerDue : DivisibilePerDue) : NumeroPari := NumeroPari:PropDivisibilePerDue:ProphEquivalenza:NumeroPari DivisibilePerDuehDivisibilePerDue:DivisibilePerDueNumeroPari NumeroPari:PropDivisibilePerDue:ProphEquivalenza:NumeroPari DivisibilePerDuehDivisibilePerDue:DivisibilePerDuehDirezione:DivisibilePerDue NumeroPariNumeroPari NumeroPari:PropDivisibilePerDue:ProphEquivalenza:NumeroPari DivisibilePerDuehDivisibilePerDue:DivisibilePerDuehDirezione:DivisibilePerDue NumeroParihNumeroPari:NumeroPariNumeroPari All goals completed! 🐙

Dopo aver estratto la direzione necessaria, la applichiamo come una normale implicazione.

10. Come scegliere una regola

Di fronte a un proof state, conviene porsi alcune domande.

La prima riguarda il goal: qual è il suo connettivo principale? Se il goal è un'implicazione, una congiunzione, una disgiunzione, una negazione o un bicondizionale, una regola di introduzione suggerisce come costruirlo.

La seconda riguarda il contesto: quali dimostrazioni composte sono già disponibili? Un'implicazione può essere applicata; una congiunzione può essere scomposta; una disgiunzione richiede casi; una negazione può essere applicata alla proposizione che nega; un bicondizionale offre due direzioni.

Le principali corrispondenze operative sono:

Tattica o termine LeanLettura nella deduzione naturale
intro hintroduciamo un'assunzione
apply Capplichiamo un costruttore o una regola
have h := ...abbiamo un nuovo fatto intermedio
exact husiamo esattamente il termine richiesto
cases h withabbiamo i diversi casi possibili
h.left, h.righteliminiamo una congiunzione
hAB hAeliminiamo un'implicazione

Questa terminologia descrive la struttura del termine che Lean sta costruendo e rende leggibile il rapporto tra regola formale, argomento in linguaggio naturale e proof script.

11. Strategia di lavoro

Quando costruiamo una dimostrazione:

1. leggiamo il goal e le assunzioni; 2. identifichiamo il connettivo principale del goal; 3. scegliamo una regola di introduzione, se il goal lo suggerisce; 4. cerchiamo nelle assunzioni una regola di eliminazione utile; 5. rendiamo espliciti con have i risultati intermedi importanti; 6. chiudiamo il goal con exact quando abbiamo un termine del tipo richiesto; 7. controlliamo che tutti i casi e tutti i sotto-goal siano stati risolti.

Il file Classroom.lean mostra ciascuna regola separatamente. Il file Exercises.lean propone prima applicazioni dirette e poi argomenti che combinano più regole. Il file Solutions.lean mostra come ogni passaggio in Lean possa essere ricondotto alla regola di deduzione naturale corrispondente.

12. Fonti e letture