Oggi proponiamo alla vostra attenzione la prima parte della traduzione del materiale su come Dropbox gestisce il controllo dei tipi di codice Python.
In Dropbox si scrive molto in Python. È il linguaggio che utilizziamo in modo estremamente esteso, sia per i servizi backend che per le applicazioni client desktop. Utilizziamo anche molto Go, TypeScript e Rust, ma Python è il nostro linguaggio principale. Considerando le nostre dimensioni, che riguardano milioni di righe di codice Python, ci siamo resi conto che la tipizzazione dinamica di tale codice ha complicato ingiustificatamente la sua comprensione e ha iniziato a influenzare seriamente la produttività. Per attenuare questo problema, abbiamo iniziato un passo dopo l'altro a convertire il nostro codice in un controllo statico dei tipi utilizzando mypy. Questo è probabilmente il sistema di controllo dei tipi autonomo più popolare per Python. Mypy è un progetto open source, i suoi principali sviluppatori lavorano in Dropbox.
Dropbox è stata una delle prime aziende a implementare il controllo statico dei tipi nel codice Python su scala simile. Oggi, mypy è utilizzato in migliaia di progetti. Questo strumento è stato testato innumerevoli volte, quello che si dice, "sul campo". Per arrivare dove ci troviamo ora, abbiamo dovuto percorrere un lungo cammino. In questo percorso ci sono stati molti tentativi falliti e esperimenti andati male. Questo materiale racconta la storia del controllo statico dei tipi in Python - dal suo difficile inizio, che faceva parte del mio progetto di ricerca scientifica, fino ai giorni nostri, quando i controlli dei tipi e i suggerimenti sui tipi sono diventati comuni per un numero incalcolabile di sviluppatori che scrivono in Python. Questi meccanismi sono ora supportati da numerosi strumenti, come IDE e analizzatori di codice.
→
Perché è necessario il controllo dei tipi?
Se hai mai utilizzato Python a tipizzazione dinamica, potresti non comprendere perché ci sia tanto rumore attorno alla tipizzazione statica e a mypy di recente. Potrebbe anche essere che ti piaccia Python proprio per la sua tipizzazione dinamica e che ciò che sta accadendo ti infastidisca. La chiave per il valore della tipizzazione statica è la scala delle soluzioni: più grande è il tuo progetto, più tendi verso la tipizzazione statica e, alla fine, più ne hai realmente bisogno.
Supponiamo che un progetto abbia raggiunto dimensioni di decine di migliaia di righe di codice e che sia gestito da più programmatori. Considerando un progetto del genere, possiamo dire, basandoci sulla nostra esperienza, che comprendere il suo codice sarà la chiave per mantenere la produttività degli sviluppatori. Senza annotazioni di tipo, è difficile capire, ad esempio, quali argomenti debbano essere passati a una funzione o quali valori di tipo possa restituire una certa funzione. Ecco alcune domande tipiche, alle quali spesso non è facile rispondere senza utilizzare annotazioni di tipo:
- Questa funzione può restituire
Nessuno? - Che tipo dovrebbe avere questo argomento
items? - Qual è il tipo dell'attributo
id:intè questo,str, oppure, forse, un tipo personalizzato? - Questo argomento deve essere una lista? Può essere passato un tuple?
Se guardiamo il seguente frammento di codice, arricchito con annotazioni di tipo, e tentiamo di rispondere a domande simili, scopriremo che è un compito molto semplice:
class Resource:
id: bytes
...
def read_metadata(self,
items: Sequence[str]) -> Dict[str, MetadataItem]:
...read_metadatanon restituisceNessuno, poiché il tipo restituito non èOptional[…].- Argomento
itemsè una sequenza di stringhe. Non può essere iterata in ordine arbitrario. - L'attributo
idè una stringa di byte.
In un mondo ideale, ci si potrebbe aspettare che tutte queste sottigliezze siano descritte nella documentazione integrata (docstring). Tuttavia, l'esperienza offre numerosi esempi di come tale documentazione sia spesso assente nel codice con cui si lavora. Anche se tale documentazione è presente, non si può fare affidamento sulla sua assoluta correttezza. Questa documentazione può essere poco chiara, imprecisa e lasciare spazio a molte interpretazioni errate. Nei grandi team o in progetti di vasta portata, questo problema può diventare estremamente grave.
Sebbene Python si comporti egregiamente nelle fasi iniziali o intermedie dei progetti, a un certo punto, progetti e aziende di successo che utilizzano Python possono dover affrontare una questione cruciale: «Dobbiamo riscrivere tutto in un linguaggio a tipizzazione statica?».
I sistemi di verifica dei tipi come mypy risolvono il problema sopra menzionato fornendo agli sviluppatori un linguaggio formale per descrivere i tipi e verificando che tali descrizioni corrispondano all'implementazione del programma (e, facoltativamente, ne verificano l'esistenza). In generale, si può dire che questi sistemi forniscono una sorta di documentazione accuratamente verificata.
L'uso di tali sistemi presenta altri vantaggi che non sono affatto trascurabili:
- Un sistema di verifica dei tipi può rilevare alcuni errori lievi (ma anche alcuni piuttosto gravi). Un esempio tipico è quando ci si dimentica di gestire un valore
Nessunoo qualche altra condizione speciale. - Il refactoring del codice diventa significativamente più semplice, poiché il sistema di verifica dei tipi spesso indica con precisione quale codice deve essere modificato. In questo modo non è necessario sperare in una copertura del 100% del codice da parte dei test, il che, in ogni caso, è di solito irrealizzabile. Non dobbiamo esaminare in profondità i rapporti di traccia degli stack per scoprire la causa di un guasto.
- Anche nei grandi progetti, mypy può spesso effettuare un controllo completo dei tipi in frazioni di secondo. L'esecuzione dei test, invece, richiede solitamente decine di secondi o addirittura minuti. Il sistema di controllo dei tipi fornisce uno feedback immediato al programmatore e gli consente di lavorare più rapidamente. Non è più necessario scrivere test unitari fragili e complessi da mantenere, che sostituiscono entità reali con mock e patch solo per ottenere risultati più rapidi nei test del codice.
IDE e editor come PyCharm o Visual Studio Code utilizzano le potenzialità delle annotazioni di tipo per fornire agli sviluppatori opzioni di completamento automatico del codice, evidenziazione degli errori e supporto per costrutti linguistici comunemente utilizzati. E questi sono solo alcuni dei vantaggi offerti dalla tipizzazione. Per alcuni programmatori, tutto ciò rappresenta l'argomento principale a favore della tipizzazione. È qualcosa che porta beneficio immediato dopo l'implementazione. Questa modalità di utilizzo dei tipi non richiede un sistema di verifica dei tipi separato, come mypy; va detto, tuttavia, che mypy aiuta a mantenere la corrispondenza tra le annotazioni di tipo e il codice.
La storia di mypy
La storia di mypy è iniziata nel Regno Unito, a Cambridge, alcuni anni prima che io mi unissi a Dropbox. Stavo esplorando, nell'ambito di un dottorato di ricerca, la questione dell'unificazione dei linguaggi staticamente e dinamicamente tipizzati. Ero ispirato da un articolo sulla tipizzazione graduale di Jeremy Siek e Walid Taha, così come dal progetto Typed Racket. Cercavo modi per usare lo stesso linguaggio di programmazione per progetti differenti, da piccoli script a basi di codice composte da milioni di righe. Volevo che in progetti di qualsiasi dimensione non fosse necessario scendere a compromessi eccessivi. Un aspetto fondamentale era l'idea di una transizione graduale da un prototipo non tipizzato a un prodotto finale staticamente tipizzato, ampiamente testato. Oggi queste idee sono in gran parte considerate scontate, ma nel 2010 era un problema ancora attivamente ricercato.
Il mio lavoro iniziale nel campo del controllo dei tipi non era mirato a Python. Invece, utilizzavo un piccolo linguaggio ‘fatto in casa’ . Ecco un esempio che ti permetterà di capire di cosa si tratta (le annotazioni dei tipi qui non sono obbligatorie):
def Fib(n as Int) as Int
if n <= 1
return n
else
return Fib(n - 1) + Fib(n - 2)
end
endL'utilizzo di un linguaggio semplificato di propria creazione è un approccio comune nelle ricerche scientifiche. Questo non è in gran parte dovuto al fatto che consente di condurre esperimenti rapidamente, ma anche perché ciò che non ha attinenza con la ricerca può essere ignorato senza problemi. I linguaggi di programmazione realmente utilizzati di solito rappresentano fenomeni complessi con implementazioni articolate, il che rallenta gli esperimenti. Tuttavia, qualsiasi risultato basato su un linguaggio semplificato appare un po' sospetto, poiché durante la sua ottenimento il ricercatore potrebbe aver sacrificato considerazioni importanti per un uso pratico dei linguaggi.
Il mio strumento di verifica dei tipi per Alore sembrava molto promettente, ma volevo testarlo eseguendo esperimenti con codice reale, che, si può dire, non era stato scritto in Alore. Fortunatamente, il linguaggio Alore si basava in gran parte sulle stesse idee di Python. È stato abbastanza semplice rielaborare lo strumento di verifica dei tipi in modo che potesse funzionare con la sintassi e la semantica di Python. Questo ha permesso di provare la verifica dei tipi nel codice open source di Python. Inoltre, ho scritto un transpiler per convertire codice scritto in Alore in codice Python e l'ho usato per tradurre il codice del mio strumento di verifica dei tipi. Ora avevo un sistema per la verifica dei tipi, scritto in Python, che supportava un sottoinsieme di Python, una sorta di variante di questo linguaggio! (Alcune scelte architetturali che avevano senso per Alore si adattavano male a Python, e ciò si nota ancora in alcune parti della base di codice di mypy.)
In realtà, il linguaggio supportato dal mio sistema di tipi in quel momento non poteva essere considerato Python: era una variante di Python a causa di alcune limitazioni nella sintassi delle annotazioni dei tipi di Python 3.
Sembrava una via di mezzo tra Java e Python:
int fib(int n):
if n <= 1:
return n
else:
return fib(n - 1) + fib(n - 2)Una delle mie idee all'epoca era di utilizzare le annotazioni di tipo per migliorare le prestazioni attraverso la compilazione di questa versione di Python in C, o forse in bytecode JVM. Ho progredito fino alla fase di scrittura di un prototipo di compilatore, ma ho abbandonato questa idea poiché la verifica dei tipi sembrava già utile di per sé.
Alla fine, ho presentato il mio progetto alla conferenza PyCon 2013 a Santa Clara. Ho anche parlato di questo con Guido van Rossum, il magnanimo dittatore a vita di Python. Mi ha convinto ad abbandonare la mia sintassi custom e attenersi alla sintassi standard di Python 3. Python 3 supporta le annotazioni delle funzioni, quindi il mio esempio poteva essere riscritto come mostrato qui sotto, ottenendo un programma Python normale:
def fib(n: int) -> int:
if n <= 1:
return n
else:
return fib(n - 1) + fib(n - 2)Ho dovuto scendere a compromessi (per primo voglio sottolineare che ho inventato una sintassi custom proprio per questo). In particolare, Python 3.3, l'ultima versione del linguaggio all'epoca, non supportava le annotazioni delle variabili. Ho discusso con Guido via e-mail diverse possibilità di formattazione sintattica per tali annotazioni. Abbiamo deciso di usare commenti per indicare i tipi delle variabili. Questo ci ha permesso di raggiungere l'obiettivo prefissato, ma sembrava un po' ingombrante (Python 3.6 ci ha dato una sintassi più gradevole):
products = [] # type: List[str] # EwwLe annotazioni con i tipi, inoltre, si sono rivelate utili per il supporto di Python 2, che non ha supporto nativo per le annotazioni di tipo:
f fib(n):
# type: (int) -> int
if n <= 1:
return n
else:
return fib(n - 1) + fib(n - 2)Si è rivelato che questi (e altri) compromessi non avevano realmente importanza — i vantaggi della tipizzazione statica hanno portato gli utenti a dimenticare in fretta la sintassi non perfetta. Poiché ora nel codice Python, dove erano controllati i tipi, non venivano utilizzate strutture sintattiche speciali, gli strumenti Python esistenti e i processi di elaborazione del codice hanno continuato a funzionare normalmente, il che ha reso molto più facile per gli sviluppatori apprendere il nuovo strumento.
Guido, inoltre, mi ha convinto a unirsi a Dropbox dopo che ho discusso la mia tesi. Qui inizia la parte più interessante della storia di mypy.
Continua…
Gentili lettori! Se utilizzi Python, ti chiediamo di raccontarci che tipo di progetti stai sviluppando in questo linguaggio.
Fonte: habr.com
