La risposta breve è che Clojure è stato progettato per utilizzare un compilatore a passaggio singolo molto semplice che legge e compila una singola espressione o forma alla volta. Nel bene e nel male non ci sono informazioni di tipo globale, nessuna inferenza di tipo globale e nessuna analisi o ottimizzazione globale. Clojure utilizza le istanze clojure.lang.Var
per creare associazioni globali mediante una serie di hashmap dai simboli testuali ai valori transazionali. I moduli def
creano tutti i collegamenti nell'ambito globale in questa mappa di associazione globale. Quindi, se in Scala una "funzione" (metodo) verrà risolta in un'istanza o in un metodo statico su una determinata classe JVM, in Clojure una "funzione" (def) è in realtà solo un riferimento a una voce nella tabella delle associazioni var. Quando viene invocata una funzione, non esiste un collegamento statico ad un'altra classe, invece la var è di riferimento con un nome simbolico, quindi viene dereferenziata per ottenere un'istanza di un oggetto clojure.lang.IFn
che viene quindi richiamato.
Questo livello di riferimento indiretto significa che è possibile rivalutare solo una singola definizione alla volta e che la rivalutazione diventa globale visibile a tutti i client della variabile ridefinita.
In confronto, quando una definizione in Scala cambia, scalac deve ricaricare il file modificato, macroexpand, digitare infer, tipo check e compilare. Quindi, a causa della semantica del caricamento di classi sulla JVM, scalac deve anche ricaricare tutte le classi che dipendono dai metodi della classe che sono cambiati. Anche tutti i valori che sono istanze della classe modificata diventano trash.
Entrambi gli approcci hanno i loro punti di forza e di debolezza. Ovviamente l'approccio di Clojure è più semplice da implementare, tuttavia paga un costo costante in termini di prestazioni a causa delle continue operazioni di ricerca delle funzioni che dimenticano i problemi di correttezza dovuti alla mancanza di tipi statici e di cosa hai. Ciò è probabilmente adatto a contesti in cui molti cambiamenti avvengono in un breve lasso di tempo (sviluppo interattivo) ma è meno adatto al contesto quando il codice è per lo più statico (distribuzione, quindi Oxcart). some work I did suggerisce che il rallentamento dei programmi Clojure dalla mancanza di collegamento del metodo statico è dell'ordine del 16-25%. Questo non è per chiamare Clojure slow o Scala veloce, hanno solo priorità diverse.
Scala sceglie di fare più lavoro in anticipo in modo che l'applicazione compilata funzioni meglio, che è probabilmente più adatta per l'implementazione dell'applicazione quando si verificherà un ricaricamento minimo o nullo, ma si rivela un trascinamento quando si vogliono apportare molte piccole modifiche .
Alcuni materiali che ho a disposizione sulla compilazione del codice Clojure più o meno cronologico per ordine di pubblicazione da quando Nicholas ha influenzato molto il mio lavoro GSoC.
che suppongo mi lascia in il luogo infelice di dire semplicemente "Mi dispiace, Scala non è stato progettato per questo nel modo in cui C lojure era "per quanto riguarda lo scambio di codice a caldo.
Uno è usato per scrivere Clojure, l'altro per scrivere Scala. Inoltre: http://stackoverflow.com/questions/2471947/is-there-an-easy-way-to-get-the-scala-repl-to-reload-a-class-or-package – vptheron
È un po 'difficile per me sapere quale sarebbe la risposta corretta? Stai forse chiedendo se il REPL di Scala può ridefinire le funzioni in un programma in esecuzione? –
Curiosità, la domanda è buona, ma il titolo non lo è. – Mars