Cosa dovrebbe effettivamente dimostrare una prova di correttezza per un typechecker?


11

Sto programmando da diversi anni, ma non ho molta familiarità con il CS teorico. Di recente ho cercato di studiare i linguaggi di programmazione e, come parte di ciò, il controllo del tipo e l'inferenza.

La mia domanda è: se provo a scrivere un'inferenza di tipo e a controllare un programma per un linguaggio di programmazione e desidero dimostrare che il mio typechecker funziona, qual è esattamente la prova che sto cercando?

In parole semplici, desidero che il mio tipo di controllo sia in grado di identificare eventuali errori in un pezzo di codice che potrebbero verificarsi in fase di esecuzione. Se dovessi usare qualcosa come Coq per provare che la mia implementazione è corretta, cosa proverà esattamente questa "prova di correttezza"?


Forse puoi chiarire se vuoi sapere (1) se la tua implementazione implementa un dato sistema di tipizzazione , o (2) se il tuo sistema di tipizzazione T previene gli errori che ritieni debbano essere? Sono domande diverse. TT
Martin Berger,

1
@MartinBerger: Ah, sembra che io abbia saltato quella differenza. La mia vera domanda probabilmente significava porle entrambe. Il contesto è che sto cercando di costruire una lingua, e per questo stavo scrivendo un typechecker. E la gente mi ha chiesto di usare un algoritmo collaudato. Mi interessava vedere quanto sarebbe stato difficile "provare" che l'algoritmo e il typechecker che stavo usando fossero "corretti". Da qui l'ambiguità nella mia domanda.
Vivek Ghaisas,

2
(1) è davvero una domanda nella verifica del programma e ha poco a che fare con la digitazione. Devo solo dimostrare che l'implementazione soddisfa le sue specifiche. Per quanto riguarda (2), per prima cosa definire cosa significa essere un errore di tipo immediato (ad es. Termini del genere 2 + "hello"sono "bloccati"). Una volta formalizzato, puoi provare il teorema della solidità del tipo. Ciò significa che nessun programma digitabile potrà mai evolversi in un errore di tipo immediato. Formalmente, si dimostra che se un programma è tipizzabile e per qualsiasi n : se M esegue n passi per diventare N , allora N non presenta un errore di tipo immediato. (1/2)MnMnNN
Martin Berger,

1
Ciò è in genere dimostrato dall'induzione su e sulla derivazione del giudizio di battitura. (2/2)n
Martin Berger,

Grazie! Sulla base della tua spiegazione, sembra che (2) sia davvero quello che stavo cercando. Potresti per favore dare una risposta? (E forse aggiungere eventuali dettagli che ritieni possano essere utili.) Lo accetterei come risposta! :)
Vivek Ghaisas il

Risposte:


10

La domanda può essere interpretata in due modi:

  • Se l'implementazione implementa un determinato sistema di tipizzazione ?T
  • T

Il primo è davvero una domanda nella verifica del programma e ha poco a che fare con la digitazione. Devo solo dimostrare che l'implementazione soddisfa le sue specifiche, vedi la risposta di Andrej.

TT

  • Innanzitutto, definisci formalmente cosa significa per un programma avere un errore di battitura immediato . Ci sono molti modi in cui questo può essere definito: dipende da te. In genere vogliamo prevenire programmi come 2 + "hello". In altre parole, è necessario definire un sottoinsieme di programmi, chiamarli Bad , che contiene esattamente i programmi con errore di digitazione immediato.

  • ΓM:α.MαΓ

    ΓM:αMNN

    Come dimostrare questo teorema dipende dai dettagli della lingua, dal sistema di battitura e dalla scelta di Bad .

MMNM

  • ΓM:αMNΓN:α

  • ΓM:αM

Si noti che non tutti i sistemi di battitura hanno "riduzione del soggetto", ad esempio i tipi di sessione. In questo caso, sono necessarie tecniche di prova più sofisticate.


20

Questa è una bella domanda! Chiede cosa ci aspettiamo dai tipi in una lingua tipizzata.

Prima nota che possiamo digitare qualsiasi linguaggio di programmazione con l' unità : basta scegliere una lettera, dire Ue dire che ogni programma ha tipo U. Questo non è terribilmente utile, ma fa un punto.

eAeAAint

Non c'è limite a quanto possano essere espressivi i tuoi tipi. In linea di principio potrebbero essere qualsiasi tipo di affermazioni logiche, potrebbero usare la teoria delle categorie e quant'altro, ecc. Ad esempio, i tipi dipendenti ti permetteranno di esprimere cose come "questa funzione mappa elenchi in modo tale che l'output sia un input ordinato". Puoi andare oltre, al momento sto ascoltando un discorso su "logiche di separazione simultanee" che ti permette di parlare di come i programmi simultanei funzionano con lo stato condiviso. Roba di fantasia.

L'arte dei tipi nella progettazione del linguaggio di programmazione è quella di bilanciare espressività e semplicità :

  • tipi più espressivi ci consentono di spiegare in modo più dettagliato (a noi stessi e al compilatore) cosa dovrebbe succedere
  • i tipi più semplici sono più facili da capire e possono essere automatizzati più facilmente nel compilatore. (Le persone escono con tipi che essenzialmente richiedono un assistente di prova e l'input dell'utente per eseguire il controllo del tipo.)

La semplicità non è da sottovalutare, poiché non tutti i programmatori hanno un dottorato in teoria dei linguaggi di programmazione.

Quindi torniamo alla tua domanda: come fai a sapere che il tuo sistema di tipi è buono ? Bene, dimostra i teoremi che mostrano che i tuoi tipi sono bilanciati. Ci saranno due tipi di teoremi:

  1. Teoremi che dicono che i tuoi tipi sono utili . Sapere che un programma ha un tipo dovrebbe implicare alcune garanzie, ad esempio che il programma non si blocchi (sarebbe un teorema di sicurezza ). Un'altra famiglia di teoremi collegherebbe i tipi a modelli semantici in modo che possiamo iniziare a usare la matematica reale per provare cose sui nostri programmi (quelli sarebbero i teoremi di adeguatezza e molti altri). L'unità di cui sopra è cattiva perché non ha teoremi così utili.

  2. Teoremi che affermano che i tuoi tipi sono semplici . Uno di base sarebbe che è decidibile se una determinata espressione ha un determinato tipo. Un'altra caratteristica di semplicità è quella di fornire un algoritmo per inferire un tipo. Altri teoremi sulla semplicità sarebbero: che un'espressione ha al massimo un tipo, o che un'espressione ha un tipo principale (cioè, il "migliore" tra tutti i tipi che ha).

È difficile essere più specifici perché i tipi sono un meccanismo molto generale. Ma spero che vedrai per cosa dovresti sparare. Come la maggior parte degli aspetti della progettazione del linguaggio di programmazione, non esiste una misura assoluta di successo. Invece, c'è uno spazio di possibilità progettuali e l'importante è capire dove sei o vuoi essere nello spazio.


Grazie per quella risposta dettagliata! Tuttavia, non sono ancora sicuro della risposta alla mia domanda. Come esempio concreto, prendiamo C, un linguaggio tipicamente statico con un sistema di tipi abbastanza semplice. Se scrivessi un typechecker per C, come dimostrerei che il mio typechecker è "corretto"? Come cambia questa risposta se invece ho scritto un controllo di tipo per Haskell, diciamo HM? Come proverei ora la "correttezza"?
Vivek Ghaisas,

1
TeATeA

Consiglierei di fare 2. e 3. come combinazione. Inoltre, dai un'occhiata a CompCert .
Andrej Bauer,

1
TeAeAe

AAe

5

Ci sono alcune cose diverse che potresti voler dire "prova che il mio typechecker funziona". Il che, suppongo, fa parte di ciò che la tua domanda sta ponendo;)

La metà di questa domanda sta dimostrando che la tua teoria dei tipi è abbastanza buona da dimostrare qualunque proprietà della lingua. La risposta di Andrej affronta molto bene questo settore. L'altra metà della domanda è: supponendo che la lingua e il suo sistema di tipi siano già corretti, come si può dimostrare che il proprio tipo di correttore di tipi effettivamente implementa correttamente il sistema di tipi? Ci sono due principali prospettive che posso vedere prendendo qui.

Uno è: come possiamo mai fidarci che una particolare implementazione corrisponda alle sue specifiche? A seconda del grado di garanzie che desideri, potresti essere soddisfatto di una grande suite di test, oppure potresti volere una sorta di verifica formale, o più probabilmente una combinazione di entrambi . L'aspetto positivo di questa prospettiva è che evidenzia davvero l'importanza di stabilire dei limiti sulle affermazioni che stai facendo: cosa significa esattamente "correggere"? quale parte del codice viene controllata, rispetto a quale parte è il TCB presunto corretto? ecc. Il rovescio della medaglia è che pensare troppo a questo proposito porta a svuotare le buche filosofiche del coniglio - beh, "svantaggio" se non ti piacciono quelle buche del coniglio.

La seconda prospettiva è una presa più matematica sulla correttezza. Quando abbiamo a che fare con le lingue in matematica, creiamo spesso "modelli" per le nostre "teorie" (o viceversa) e quindi proviamo a dimostrare: (a) tutto ciò che possiamo fare nella teoria che possiamo fare nel modello, e (b) tutto ciò che possiamo fare nel modello che possiamo fare nella teoria. (Questi sono solidità e completezzateoremi. Quale dipende dal fatto che tu abbia "iniziato" dalla teoria sintattica o dal modello semantico.) Con questa mentalità possiamo pensare alla tua implementazione del controllo del tipo come un modello particolare per la teoria del tipo in questione. Quindi vorresti dimostrare questa corrispondenza a doppio senso tra ciò che la tua implementazione può fare e ciò che la teoria dice che dovresti essere in grado di fare. L'aspetto positivo di questa prospettiva è che si concentra davvero sul fatto che tu abbia coperto tutti i casi angolari, se la tua implementazione è completa nel senso di non tralasciare alcun programma che dovrebbe accettare come sicuro per il tipo e se la tua implementazione è valida il senso di non far entrare in nessun programma dovrebbe rifiutare come mal tipizzato. Il rovescio della medaglia è che la prova della corrispondenza è probabilmente separata dall'implementazione stessa,


Non sono sicuro di essere d'accordo con "il lato positivo di questa prospettiva è che si concentra davvero sul fatto che tu abbia coperto tutti i casi angolari", specialmente se il modello è solo solido, ma non completo. Proporrei una prospettiva diversa: passare attraverso un modello è una tecnica di prova contingente che usi per vari motivi, ad esempio perché il modello è più semplice. Non c'è nulla di filosoficamente più dignitoso nell'attraversare un modello - alla fine vuoi sapere dell'eseguibile reale e del suo comportamento.
Martin Berger,

Pensavo che "modello" e "teoria" fossero intesi in senso lato, e Wren stava solo sottolineando l'importanza di provare a stabilire una corrispondenza a due vie tramite un "teorema di solidità + completezza". (Penso anche che sia importante e abbia fatto un commento al post di Andrej.) È vero che in alcune situazioni potremo solo dimostrare un teorema di solidità (o un teorema di completezza, a seconda della tua prospettiva), ma con entrambe le direzioni in mente è un utile vincolo metodologico.
Noam Zeilberger,

1
@NoamZeilberger "La domanda è," disse Martin, "se riesci a far sì che le parole significino così tante cose diverse."
Martin Berger,

Quando ho imparato a conoscere i sistemi di tipizzazione e la semantica del linguaggio di programmazione, ho scoperto che i modelli sono semplicemente tecniche di prova sulla semantica operativa, piuttosto che fini a se stessi, sublimemente liberatori.
Martin Berger,

1
Il collegamento di diversi modelli attraverso la solidità e la completezza è un'importante metodologia scientifica per il trasferimento di informazioni.
Martin Berger,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.