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 m
e Vec a n
, essendo vettori con tipo di elemento, a
lunghezze m
e n
rispettivamente, 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 m
e n
, quindi le lunghezze dei vettori possono essere dinamiche.