Esistono alternative ai tipi per l'analisi statica?


18

La digitazione statica in un linguaggio di programmazione può essere utile per applicare determinate garanzie in fase di compilazione, ma i tipi sono l'unico strumento per questo lavoro? Ci sono altri modi per specificare gli invarianti?

Ad esempio, un linguaggio o un ambiente potrebbero aiutare a far valere una garanzia in merito alla lunghezza dell'array o alle relazioni tra gli input di una funzione. Non ho mai sentito niente di simile al di fuori di un sistema di tipi.

Una cosa correlata che mi chiedevo è se ci sono modi non dichiarativi per fare analisi statiche (i tipi sono dichiarativi, per la maggior parte ).

Risposte:


24

I sistemi di tipo statico sono una specie di analisi statica, ma esistono molte analisi statiche che generalmente non sono codificate in sistemi di tipo. Per esempio:

  • Il controllo del modello è una tecnica di analisi e verifica per sistemi concorrenti che consente di dimostrare che il programma è ben educato in tutti i possibili intrecci di thread.

  • L'analisi del flusso di dati raccoglie informazioni sui possibili valori delle variabili, che possono determinare se alcuni calcoli sono ridondanti o se alcuni errori non vengono considerati.

  • L'interpretazione astratta modella in modo conservativo gli effetti di un programma, di solito in modo tale che l'analisi si concluda: le verificatrici del tipo possono essere implementate in modo simile agli interpreti astratti.

  • La logica di separazione è una logica di programma (utilizzata ad esempio nell'analizzatore Infer ) che può essere utilizzata per ragionare sugli stati del programma e identificare problemi come dereferenze del puntatore null, stati non validi e perdite di risorse.

  • La programmazione basata su contratto è un mezzo per specificare precondizioni, postcondizioni, effetti collaterali e invarianti. Ada ha supporto nativo per i contratti e può verificarne alcuni staticamente.

I compilatori di ottimizzazione eseguono molte piccole analisi al fine di creare strutture di dati intermedie da utilizzare durante l'ottimizzazione, come SSA, stime dei costi di allineamento, informazioni sull'associazione delle istruzioni e così via.

Un altro esempio di analisi statica non dichiarativa si trova nel typechecker di Hack , dove i normali costrutti del flusso di controllo possono affinare il tipo di una variabile:

$x = get_value();
if ($x !== null) {
    $x->method();    // Typechecks because $x is known to be non-null.
} else {
    $x->method();    // Does not typecheck.
}

E parlando di "raffinazione", nella terra dei sistemi di tipi , i tipi di raffinamento (come usati in LiquidHaskell ) accoppiano i tipi con predicati che sono garantiti per le istanze del tipo "raffinato". E i tipi dipendenti lo spingono oltre, consentendo ai tipi di dipendere dai valori. Il "ciao mondo" della tipizzazione dipendente è di solito la funzione di concatenazione di array:

(++) : (a : Type) -> (m n : Nat) -> Vec a m -> Vec a n -> Vec a (m + n)

Qui, ++prende due operandi di tipo Vec a me Vec a n, essendo vettori con tipo di elemento, alunghezze me nrispettivamente, che sono numeri naturali ( Nat). Restituisce un vettore con lo stesso tipo di elemento la cui lunghezza è m + n. E questa funzione dimostra questo vincolo in modo astratto, senza conoscere i valori specifici di me n, quindi le lunghezze dei vettori possono essere dinamiche.


Che cos'è un sistema di tipi? Mi rendo conto che in realtà non lo so. La definizione su Wikipedia è circolare: en.wikipedia.org/wiki/Type_system
Max Heiber

1
@mheiber: Un sistema di tipo statico è semplicemente un'analisi statica che attribuisce tipo (per esempio, int, int -> int, forall a. a -> a) per termini (ad esempio, 0, (+ 1), \x -> x). Altre analisi possono attribuire proprietà diverse non correlate al tipo di dati, ad esempio effetti collaterali ( pure, io), visibilità ( public, private) o stato ( open, closed). In pratica, molte di queste proprietà possono essere verificate nella stessa implementazione del controllo / inferenza del tipo, quindi la distinzione non è del tutto chiara.
Jon Purdy,

4

La risposta di JonPurdy è migliore, ma vorrei aggiungere qualche altro esempio:

ovvio:

  • controllo della sintassi

  • rilascio di fibre

Non ovvio:

  • Rust consente al programmatore di specificare se i "binding" sono mutabili e applica questi vincoli.

  • Questo è in qualche modo correlato: alcune lingue consentono l'esecuzione di un codice in fase di compilazione, il che significa che molte cose che altrimenti sarebbero errori di runtime possono essere rilevate in fase di compilazione. Alcuni esempi sono le macro e le procedure del linguaggio Nim contrassegnate con il compileTimepragma .

  • La programmazione logica sta fondamentalmente costruendo un programma fornendo affermazioni.

Digitazione semi-statica:

  • Il flusso di Facebook consente un ibrido tra la digitazione dinamica e statica. L'idea è che anche il codice dinamico sia tipizzato in modo implicito. Flow consente di eseguire un server che controlla il codice mentre viene eseguito per rilevare possibili errori di tipo, anche se non si annota il tipo di funzioni.

1

L'analisi del tipo non significa molto.

Agda è noto per avere un sistema di tipo completo di Turing, molto diverso (e molto più difficile da calcolare) rispetto alle lingue ML (ad esempio Ocaml ).


Agda, fa di tutto per non avere un "sistema di tipi completo di Turing" e nemmeno un "sistema di termini completo di Turing".
user833970
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.