Le chiusure sono un modo efficace per implementare funzioni.
Affermo che ogni funzione è concettualmente una chiusura, anche nelle poche lingue che non le possiedono.
Le variabili chiuse sono quindi costanti o dati statici all'interno del codice. Ma in chiusure a tutti gli effetti (come in Ocaml, Scheme, Common Lisp o C ++ 11) le variabili chiuse e il codice sono nella chiusura stessa.
Ad esempio, in C (e in C ++ 98) le funzioni sono chiusure, ma le loro variabili chiuse (o gratuite ) sono limitate a static
o variabili globali.
Chiusure offre anche la possibilità di funzioni anonime, ad es. lambda, che creano chiusure con nuove variabili chiuse (e questo è qualcosa che non è possibile nello standard C). Questa è l'operazione astrazione di λ-calculus (che non esiste in C, questo è il motivo per cui ogni funzione di callback -eg in GTK- richiede alcuni "dati client" come parametro aggiuntivo).
Da un punto di vista dell'implementazione, una funzione è sempre un codice con alcuni dati richiesti dal codice. I dati sono le variabili chiuse. Per il codice C compilato, i dati sono cablati nel codice come variabili fisse globali o statiche (e la modifica di queste variabili richiede la ricompilazione) o come costanti letterali. Quindi l'unico modo di fare un'astrazione sarebbe di rigenerare qualche nuovo codice con alcune nuove variabili in esso.
L'astrazione non esiste nello standard C perché non c'è modo di generare nuove funzioni portabili. Ma potresti usare strani trucchi specifici per l'implementazione. Immagina di voler implementare la funzione del generatore di traduzioni. In ocaml, la funzione per generare la funzione translate-by delta
è
let transl delta = fun x -> x + delta
e questo sta generando una nuova chiusura per ogni invocazione. Quindi in
let t3 = transl 3 in t3 5
viene generata una nuova chiusura t3
(ma forse l'ottimizzatore la sta rimuovendo) e il risultato è 8 (che è 3 + 5).
Potresti fare trucchi fasulli in C: su Linux, per esempio generare al runtime un nuovo file testuale t3mod.c
contenente
int t3(int y) { return y+3; };
e dovresti compilare quel file t3mod.c
in un oggetto condiviso t3mod.so
e dlopen
esso poi dlsym
usando "t3"
. Questo modo forzato (usando la generazione del codice C in runtime, quindi caricandolo dinamicamente) è un modo di implementare le astrazioni in C ed è un modo per generare dinamicamente nuovi puntatori di funzione. Allo stesso modo, potresti utilizzare le librerie JIT che compilano come ad es. libjit , LLVM (o < a href="https://gcc.gnu.org/wiki/JIT"> libgccjit
in futuro GCC 5). Leggi anche la valutazione parziale & eval & programmazione multistadio (ad es. Meta OCaml ...). Vedi anche le voci del blog di J.Pitrat su meta -commutazione combinata & meta-bug .