Che tipo di controlli esegue Raku in fase di compilazione? Potrebbe cambiare in futuro?
Attualmente (a partire da agosto 2020) Rakudo non controlla i valori di ritorno delle funzioni in fase di compilazione; cioè, non fornisce garanzie statiche che le funzioni soddisfino i loro vincoli di ritorno. In concreto, le seguenti due funzioni vengono compilate entrambe come Raku:
sub get-int(--> Int) { 'bug' }
sub get-int($a --> Int} { when $a == 5 { 'Rare bug' }
default { 42 }
}
Ho due domande correlate:
C'è un modo per sapere quale (se esiste) il controllo del tipo avviene attualmente in fase di compilazione? (O tramite un elenco che qualcuno ha scritto, da qualche parte nei documenti, o un posto centrale nella fonte Rakudo) O è più ad hoc di così?
Questa mancanza di tempo di compilazione per il controllo dei tipi è una decisione intenzionale di progettazione? O sta aggiungendo un controllo di battitura più statico qualcosa che sarebbe bello avere un giorno, ma non è ancora stato implementato?
(Ho familiarità con l'ottima risposta di Johnathan a Le penalità sulle prestazioni per tipi / vincoli in Raku?, Che afferma che "Raku impone che i vincoli di tipo scritti nel programma vengano applicati al massimo in fase di esecuzione ." Questa risposta descrive vari modi per evitare l'esecuzione -costi di tempo dei controlli dei tipi, ma non descrive quali, se ce ne sono, i controlli dei tipi vengono eseguiti in fase di compilazione (il che eviterebbe sicuramente i costi di runtime!).)
Risposte
Al momento in fase di compilazione viene eseguito pochissimo controllo dei tipi; ciò che è per lo più avviene come effetto collaterale dell'ottimizzatore statico. I controlli odierni riguardano principalmente le chiamate di subroutine, dove:
- Possiamo determinare l'arità della chiamata e sapere che il numero di argomenti passati non corrisponderà mai
- Abbiamo argomenti letterali e possiamo vedere che non potrebbero mai corrispondere alla firma
Questo è un residuo di quando l'ottimizzatore statico ha svolto più lavoro in linea. Al giorno d'oggi, integra solo gli operatori nativi in fase di compilazione e lascia il resto all'ottimizzatore dinamico della VM, che è molto più capace di inline e può anche uninline (consentendo l'ottimizzazione speculativa, ma significa anche che le tracce dello stack originali possono essere recuperate, mentre il l'ottimizzatore statico ha perso queste informazioni).
Fare di più in fase di compilazione è considerato desiderabile, tuttavia ci sono alcuni problemi pratici da considerare.
- L'introduzione di controlli aggiuntivi può anche introdurre la rottura del codice che funzionava prima. Si consideri un modulo con un percorso del codice che non supererebbe un controllo più rigoroso in fase di compilazione, ma che viene utilizzato in sistemi che non si verificano mai in quel caso. Se ha iniziato a non riuscire a compilare su versioni più recenti del compilatore, sarebbe diventato impossibile distribuire quel sistema dopo un aggiornamento del compilatore. In generale, ciò significa che i controlli eseguiti dovrebbero cambiare sulle modifiche alla versione della lingua. (Ciò significa comunque che le persone dovrebbero dichiarare la versione della lingua contro cui stanno scrivendo quando scrivono il codice, attenzione.)
- Che più controlli in fase di compilazione "eviteranno certamente i costi di runtime" può essere vero, ma non è banale ragionare. Un runtime gestito non può fidarsi ciecamente delle promesse fatte nel bytecode che viene dato, poiché ciò potrebbe portare a violazioni della sicurezza della memoria (che portano a SIGSEGV o peggio). Questo è abbastanza chiaramente vero in un linguaggio come Raku, dove la semantica del controllo del tipo è programmabile, ma è vero su JVM, CLR e così via. Le maggiori vittorie relative ai tipi in Raku derivano dall'uso di tipi nativi, che possono evitare molte allocazioni e quindi il lavoro di garbage collection.
- L'implementazione di ulteriori controlli aumenterà la complessità del compilatore e anche la quantità di tempo necessaria per la compilazione. Il primo di questi è già un problema; il frontend del compilatore non ha visto cambiamenti architettonici significativi in circa un decennio. L'attuale lavoro di RakuAST che getta le basi per le macro implica anche una quasi riscrittura del frontend del compilatore. L'architettura migliorata dovrebbe facilitare l'implementazione di ulteriori controlli del tipo in fase di compilazione, ma si sta anche studiando come parallelizzare gli aspetti della compilazione, il che potrebbe consentire al compilatore di fare di più senza aumentare il tempo di compilazione del wallclock.
Una volta completata l'attuale revisione del frontend del compilatore, sembra abbastanza probabile che vengano introdotti più controlli in fase di compilazione (ma abilitati solo dalla versione in lingua successiva), almeno, a condizione che qualcuno ci lavori.
Tuttavia, c'è un'opportunità ancora più eccitante in arrivo in quest'area: poiché ci sarà un'API per i programmi Raku e con i piani che si uniscono per i passaggi del compilatore personalizzati, presto sarà anche possibile implementare i controlli di tipo come moduli ! Alcuni di questi possono portare a controlli che lo rendono nelle future versioni in lingua Raku. Altri possono essere abbastanza specifici del dominio e mirati a consentire un uso più corretto di un dato modulo. Altri possono imporre rigori che non sono nello spirito della lingua di base, ma che alcuni utenti della lingua potrebbero desiderare di aderire.