Esistono tre approcci principali (di cui sono a conoscenza) per l'implementazione di funzioni di ordine superiore. Defunzionalizzazione, conversione chiusura / sollevamento lambda e combinatori.
Scriviamo A⇒B per il tipo di una funzione di ordine superiore da A per B e A→Bper il tipo di puntatori a funzioni in stile C daA per B. (Se volessimo formalizzare questo, potremmo dire che l'astrazione del puntatore a funzione è consentita solo in un ambiente vuoto.)
La conversione di chiusura è l'idea che scegliamo la rappresentazione
A⇒B≡∃E.(E,(E,A)→B)
Il
Esarà in genere la tupla contenente i valori delle variabili libere nel sito di astrazione lambda. Potrebbe essere un'altra rappresentazione però.
Il sollevamento lambda adotta un approccio un po 'diverso e più globale in cui un'astrazione lambda viene tirata verso l'esterno nel contenere gli ambiti aggiungendo variabili libere come parametri lungo il percorso, fino a raggiungere l'ambito di livello superiore. Mentre questo si occupa della strutturazione a blocchi, per gestire effettivamente gli usi di ordine superiore delle funzioni è necessario consentire un'applicazione parziale. È quindi possibile passare attorno a funzioni parzialmente applicate, ma questa è sostanzialmente la stessa rappresentazione della conversione di chiusura.
Se si desidera eliminare i puntatori a funzioni, è possibile utilizzare la defunzionalizzazione, che, in questo caso speciale, produce semplicemente un elenco. Tuttavia, ci sono pochi motivi per farlo, poiché i puntatori a funzione sono costrutti naturali nella maggior parte dei linguaggi di assemblaggio.
Il prossimo approccio è usare i combinatori. Questo è fondamentalmente lo stesso del sollevamento lambda e dell'utilizzo di applicazioni parziali ad eccezione di un set fisso di funzioni di livello superiore e tutte le altre funzioni sono espresse come combinazioni di quelle. (Se non hanno un set fisso predefinito di combinatori, questo di solito è solo un approccio basato sul sollevamento lambda come ho descritto sopra.) Una funzione di ordine superiore verrebbe quindi rappresentata efficacemente da un valore in un tipo di dati usando la sintassi di Haskell come il seguente (usando SK
combinatori ):
data CA = S | K | App CA CA -- plus other things in reality, like primitive values
Una rappresentazione più simile al calcolo della colonna vertebrale probabilmente ha più senso per l'efficienza. Oppure potresti fare qualcosa del tipo:
data CA = S0 | S1 CA | S2 CA CA | K0 | K1 CA
L'applicazione di una funzione di ordine superiore si divide in due casi: o un combinatore è stato completamente applicato e quindi dovrebbe essere eseguito, oppure restituiamo un nuovo valore che rappresenta l'applicazione (parziale).
Non ho fatto un sondaggio esaustivo, ma sono abbastanza fiducioso che le variazioni sulla conversione delle chiusure sono di gran lunga la strategia di implementazione più comune per le funzioni di ordine superiore (da cui spesso vengono chiamate "chiusure"). Ha le belle proprietà di essere modulare, semplice e ragionevolmente efficiente anche nella sua forma più ingenua. Ci vuole una buona scelta di combinatori di base e un po 'di intelligenza per ottenere buoni risultati basati su combinatori. La defunzionalizzazione non è ampiamente utilizzata per quanto posso dire, ma ci sono poche ragioni per non trarre vantaggio dai puntatori a funzioni. Nella misura in cui lo fai, ad esempio invece di una grande analisi di caso hai una tabella di puntatori di funzioni in cui indicizzi, hai praticamente ricreato la conversione di chiusura.
Ci sono altri approcci. Uno è l'istanza del modello che è sostanzialmente da prendereβ-riduzione letteralmente, e semplicemente sostituendo letteralmente i termini con altri termini. Di solito, questo richiede di avere e manipolare una struttura ad albero di sintassi astratta. Le funzioni di ordine superiore sono quindi rappresentate dai loro termini lambda (sintattici) o dai loro "modelli" che possono semplificare l'esecuzione delle sostituzioni.