Non ho mai capito dichiarazioni come questa. Ad essere onesti, anche se dichiari il tipo restituito di una funzione, puoi e lo dimenticherai dopo aver scritto molte righe di codice, e dovrai comunque tornare alla riga in cui è dichiarato usando la funzione di ricerca di il tuo editor di testo per verificarlo.
Non si tratta di dimenticare il tipo restituito: questo accadrà sempre. Si tratta dello strumento in grado di farti sapere che hai dimenticato il tipo restituito.
Inoltre, poiché le funzioni sono dichiarate con il tipo funcname()...
, senza sapere il tipo dovrai cercare su ogni riga in cui viene chiamata la funzione, perché sai solo funcname
, mentre in Python e simili potresti cercare def funcname
o function funcname
cosa succede solo una volta , alla dichiarazione.
Questa è una questione di sintassi, completamente estranea alla tipizzazione statica.
La sintassi della famiglia C è davvero ostile quando vuoi cercare una dichiarazione senza avere strumenti specializzati a tua disposizione. Altre lingue non hanno questo problema. Vedi la sintassi della dichiarazione di Rust:
fn funcname(a: i32) -> i32
Inoltre, con REPL è banale testare una funzione per il suo tipo restituito con input diversi, mentre con linguaggi digitati staticamente dovresti aggiungere alcune righe di codice e ricompilare tutto solo per sapere il tipo dichiarato.
Qualsiasi lingua può essere interpretata e qualsiasi lingua può avere un REPL.
Quindi, oltre a conoscere il tipo di ritorno di una funzione che chiaramente non è un punto di forza dei linguaggi tipizzati staticamente, in che modo la tipizzazione statica è davvero utile nei progetti più grandi?
Risponderò in modo astratto.
Un programma è composto da varie operazioni e tali operazioni sono strutturate come sono a causa di alcune ipotesi fatte dallo sviluppatore.
Alcune ipotesi sono implicite e alcune sono esplicite. Alcune ipotesi riguardano un'operazione vicino a loro, altre riguardano un'operazione lontana da loro. Un'ipotesi è più facile da identificare quando viene espressa in modo esplicito e il più vicino possibile ai luoghi in cui conta il suo valore di verità.
Un bug è la manifestazione di un presupposto che esiste nel programma ma non è valido per alcuni casi. Per rintracciare un bug, dobbiamo identificare l'ipotesi errata. Per rimuovere il bug, è necessario rimuovere tale presupposto dal programma o modificare qualcosa in modo che il presupposto sia effettivamente valido.
Vorrei classificare le ipotesi in due tipi.
Il primo tipo sono le ipotesi che possono o non possono valere, a seconda degli input del programma. Per identificare un'ipotesi errata di questo tipo, dobbiamo cercare nello spazio di tutti i possibili input del programma. Utilizzando ipotesi educate e pensiero razionale, possiamo restringere il problema e cercare in uno spazio molto più piccolo. Tuttavia, man mano che un programma cresce anche un po ', il suo spazio di input iniziale cresce a un ritmo enorme, al punto che può essere considerato infinito per tutti gli scopi pratici.
Il secondo tipo sono le ipotesi che valgono sicuramente per tutti gli input o che sono decisamente errate per tutti gli input. Quando identifichiamo un'ipotesi di questo tipo come errata, non abbiamo nemmeno bisogno di eseguire il programma o testare alcun input. Quando identifichiamo un'ipotesi di questo tipo come corretta, abbiamo un sospetto in meno di cui preoccuparci quando stiamo rintracciando un bug ( qualsiasi bug). Pertanto, è utile avere quante più ipotesi possibili appartengano a questo tipo.
Per mettere un'ipotesi nella seconda categoria (sempre vera o sempre falsa, indipendentemente dagli input), abbiamo bisogno di una quantità minima di informazioni per essere disponibili nel luogo in cui viene fatta l'assunzione. Attraverso il codice sorgente di un programma, le informazioni diventano obsolete abbastanza rapidamente (ad esempio, molti compilatori non eseguono analisi interprocedurali, il che rende qualsiasi chiamata un limite rigido per la maggior parte delle informazioni). Abbiamo bisogno di un modo per mantenere aggiornate le informazioni richieste (valide e vicine).
Un modo è quello di avere la fonte di queste informazioni il più vicino possibile al luogo in cui verranno consumate, ma ciò può essere poco pratico per la maggior parte dei casi d'uso. Un altro modo è ripetere le informazioni frequentemente, rinnovandone la pertinenza attraverso il codice sorgente.
Come puoi già immaginare, i tipi statici sono esattamente questo: i fari di informazioni di tipo sparsi nel codice sorgente. Tali informazioni possono essere utilizzate per inserire la maggior parte delle ipotesi sulla correttezza del tipo nella seconda categoria, il che significa che quasi tutte le operazioni possono essere classificate come sempre corrette o sempre errate rispetto alla compatibilità dei tipi.
Quando i nostri tipi non sono corretti, l'analisi ci fa risparmiare tempo portando il bug alla nostra attenzione in anticipo piuttosto che in ritardo. Quando i nostri tipi sono corretti, l'analisi ci fa risparmiare tempo assicurandoci che quando si verifica un bug, possiamo immediatamente escludere errori di tipo.