Perché non stiamo ricercando di più verso le garanzie del tempo di compilazione?


12

Adoro tutto ciò che è tempo di compilazione e mi piace l'idea che una volta compilato un programma vengano fatte molte garanzie sulla sua esecuzione. In generale, un sistema di tipo statico (Haskell, C ++, ...) sembra offrire maggiori garanzie in fase di compilazione rispetto a qualsiasi sistema di tipo dinamico.

Da quanto ho capito, Ada va ancora oltre per quanto riguarda il controllo del tempo di compilazione ed è in grado di rilevare una maggiore gamma di bug prima dell'esecuzione. È anche considerato abbastanza sicuro, dato che, ad un certo punto, è stato scelto per campi delicati (quando gli errori di programmazione possono costare la vita delle persone).

Ora, mi chiedo: se garanzie statiche più forti portano a un codice più documentato e sicuro, allora perché non stiamo ricercando di più in quella direzione?

Un esempio di qualcosa che sembra mancare sarebbe un linguaggio che invece di definire un inttipo generico con un intervallo determinato dal numero di bit dell'architettura sottostante, si potrebbero avere intervalli (nell'esempio seguente viene Int [a..b]descritto un tipo intero tra aeb inclusi):

a : Int [1..24]
b : Int [1..12]
a + b : Int [2..36]
a - b : Int [-11..23]
b - a : Int [-23..11]

o (prendendo questo da Ada):

a : Int [mod 24]
b : Int [mod 24]
a + b : Int [mod 24]

Questa lingua selezionerebbe il tipo di base migliore per l'intervallo e eseguirà il controllo del tempo di compilazione sulle espressioni. In modo che, ad esempio, dato:

a : Int [-3..24]
b : Int [3..10]

poi:

a / b

non sarà mai definito.

Questo è solo un esempio, ma credo che ci sia molto di più che possiamo applicare al momento della compilazione. Quindi, perché ci sono così poche ricerche su questo? Quali sono i termini tecnici che descrivono questa idea (in modo che io possa trovare maggiori informazioni su questo argomento)? Quali sono i limiti?


2
Pascal ha tipi di subrange interi (ad esempio, anni '60), ma sfortunatamente la maggior parte delle implementazioni li controlla solo in fase di esecuzione (int (-1..4) è un'assegnazione compatibile con int (100..200) al momento della compilazione). Ci sono vantaggi limitati da ciò e la programmazione basata su contratto estende l'idea in una direzione migliore (Eiffel, per esempio). Lingue come C # cercano di ottenere alcuni di quei benefici con gli attributi, non li ho usati, quindi non sono sicuro di quanto siano utili nella pratica.

1
@ Ӎσᶎ: gli attributi in C # sono solo classi di metadati, quindi qualsiasi validazione dei dati verrebbe eseguita in fase di esecuzione.
Robert Harvey,

8
Come fai a sapere che ci sono poche ricerche su questo? Prova a cercare su Google dependent typeo refinement type.
Phil

3
Concordo sul fatto che la premessa sembra essere viziata; questo è certamente un campo di ricerca attivo. Il lavoro non è mai finito . Quindi, non vedo bene come si possa rispondere a questa domanda.
Raffaello

1
@Robert Harvey: Il fatto che ADA offra più garanzie non significa che il compilatore rileverà tutti gli errori, ma renderà solo gli errori meno probabili.
Giorgio,

Risposte:


11

Non sono in grado di dire quante ulteriori ricerche dovrebbero essere fatte sull'argomento, ma posso dirti che è in corso una ricerca, ad esempio il programma Verisoft XT finanziato dal governo tedesco.

I concetti che penso tu stia cercando sono chiamati verifica formale e programmazione basata su contratto , in cui quest'ultimo è un modo facile da programmare per il primo. Nella programmazione basata su contratto scrivi prima il tuo codice normalmente e poi inserisci i cosiddetti contratti nel codice. Un linguaggio facilmente utilizzabile che si basa su questo paradigma è Spec # di Microsoft Research e l' estensione di Contratti di codice funzionalmente simile ma leggermente meno carina per C # che puoi provare online (hanno anche strumenti simili per altre lingue, dai un'occhiata a rise4fun ). L '"int con tipo di intervallo" che hai citato si rifletterebbe in due contratti in una funzione:

Contract.Requires(-3 <= a && a <= 24);
Contract.Requires( 3 <= b && b <= 10);

Se si desidera chiamare quella funzione, è necessario utilizzare i parametri che sono garantiti per soddisfare questi criteri o si ottiene un errore di tempo di compilazione. Quanto sopra sono contratti molto semplici, è possibile inserire quasi tutte le assunzioni o i requisiti relativi alle variabili o alle eccezioni e alla loro relazione a cui si potrebbe pensare e il compilatore verificherà se ogni requisito è coperto da un presupposto o da qualcosa che può essere garantito, ovvero derivato dai presupposti. Questo è il motivo per cui il nome deriva: la chiamata fa i requisiti , il chiamante assicura che questi siano soddisfatti, come in un contratto commerciale.

P(x1,x2,...,xn)nPviene usato. Dal lato CS, queste due parti sono le parti critiche del processo: la generazione di condizioni di verifica è complicata e SMT è un problema NP completo o indecidibile, a seconda delle teorie considerate. Esiste persino una competizione per i risolutori di SMT, quindi sicuramente ci sono alcune ricerche in merito. Inoltre, ci sono approcci alternativi all'utilizzo di SMT per la verifica formale come l'enumerazione dello spazio degli stati, il controllo del modello simbolico, il controllo del modello limitato e molti altri che vengono anche studiati, anche se SMT è, attualmente, l'approccio più "moderno".

Per quanto riguarda i limiti dell'idea generale:

  • Come precedentemente affermato, dimostrare la correttezza del programma è un problema computazionalmente difficile, quindi potrebbe essere possibile che il controllo del tempo di compilazione di un programma con contratti (o un'altra forma di specifica) impieghi molto tempo o potrebbe essere addirittura impossibile. Applicare l'euristica che funziona bene la maggior parte delle volte è la cosa migliore che si possa fare al riguardo.
  • Più si specifica sul programma, maggiore è la probabilità di avere bug nella specifica stessa . Questo può portare a falsi positivi (il controllo del tempo di compilazione fallisce anche se tutto è privo di bug) o alla falsa impressione di essere al sicuro, anche se il tuo programma ha ancora dei bug.
  • Scrivere contratti o specifiche è un lavoro davvero noioso e la maggior parte dei programmatori è troppo pigra per farlo. Prova a scrivere un programma C # con contratti di codice ovunque, dopo un po 'penserai "dai, è davvero necessario?". Questo è il motivo per cui la verifica formale viene in genere utilizzata solo per la progettazione hardware e sistemi critici per la sicurezza, come software che controllano aeroplani o automobili.

Un'ultima cosa degna di nota che non si adatta perfettamente alla spiegazione sopra è un campo chiamato "Teoria della complessità implicita", ad esempio questo documento . Ha lo scopo di caratterizzare le lingue di programmazione in cui ogni programma che è possibile scrivere rientra in una specifica classe di complessità, ad esempio P. In tale lingua, ogni programma che si scrive viene automaticamente "assicurato" di avere un runtime polinomiale, che può essere "verificato" al momento della compilazione semplicemente compilando il programma. Non conosco alcun risultato praticamente utilizzabile di questa ricerca, tuttavia, ma sono anche lungi dall'essere un esperto.


Non dovrebbe essere possibile generare tipi o contratti dipendenti da una combinazione di test di esempio e codice non tipizzato, data una certa "strategia" che puoi scegliere in base al tuo progetto?
aoeu256,
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.