Hai un paio di proprietà stateful. Questi sono i registri e la memoria. E abbiamo una transizione tra stati. Questo è ogni ciclo della CPU. Possiamo quindi modellare questa architettura in modo semplice:
function cycle(state: State) -> State { ... }
La nostra funzione cycle
tratterà l'input state
come un oggetto immutabile e creerà un nuovo stato da restituire. Ciò implica copiare la maggior parte dei dati nel nuovo stato. Questo non è un problema finché non inizi a preoccuparti delle prestazioni.
La CPU esegue un ciclo fino al raggiungimento di uno stato di arresto. Questo potrebbe essere espresso come un loop:
while(! state.isHaltingState()) {
state = cycle(state);
}
che ovviamente potrebbe anche essere espresso come ricorsione infinita.
All'interno della nostra funzione cycle
, localizziamo, decodifichiamo ed eseguiamo l'istruzione corrente. Tutte le istruzioni modificheranno almeno il puntatore dell'istruzione. In JavaScript (che non è un puro linguaggio funzionale), l'approccio sarebbe:
- Crea una copia dei vecchi registri
- Esegui l'istruzione corrente, che può mutare i registri correnti
- Tratta i registri come immutabili e aggiungili allo stato successivo
- Restituisce lo stato successivo.
Diversamente dai registri, la memoria (= una matrice di celle di memoria, ad esempio byte) non verrà necessariamente modificata durante ciascuna istruzione. Se la memoria non è stata toccata, non dobbiamo eseguire una copia e possiamo semplicemente fare riferimento alla memoria precedente nello stato successivo. Se cambiamo una cella, dobbiamo creare una copia aggiornata da utilizzare nel nuovo stato. L'impatto di questo può essere ridotto dividendo la memoria in pagine e sostituendo solo le pagine che sono state effettivamente toccate. In effetti, questo approccio trasforma la matrice in un albero n-ary di matrici di profondità fissa.
Modellerei ogni istruzione come una funzione che calcola un delta dallo stato corrente. In questo modo, il codice per applicare il delta allo stato corrente e restituire un nuovo stato in modo efficace non deve essere ripetuto in tutto il codice. Un delta potrebbe essere un piccolo oggetto come { registers: { eax: 0x42}, memory: {0x1234: 0x00} }
.