Cosa sono le funzioni in scena (concettualmente)?


24

In un recente articolo CACM [1], gli autori presentano un'implementazione per funzioni organizzate . Usano il termine come se fosse ben noto, e nessuno dei riferimenti sembra un'ovvia introduzione.

Danno una breve spiegazione (l'enfasi sulla mia e il numero di riferimento sono cambiati; è 22 nell'originale)

Nel contesto della generazione di programmi, la programmazione multistadio (MSP, in breve la stadiazione), come stabilito da Taha e Sheard [2], consente ai programmatori di ritardare esplicitamente la valutazione di un'espressione del programma in una fase successiva (quindi, mettere in scena un'espressione). La fase attuale funge effettivamente da generatore di codice che compone (ed eventualmente esegue) il programma della fase successiva.

Tuttavia, Taha e Sheard scrivono (sottolineatura mia):

Un programma in più fasi coinvolge la generazione, la compilazione e l'esecuzione del codice, tutto all'interno dello stesso processo. Le lingue multi-stage esprimono programmi multi-stage. La stadiazione, e di conseguenza la programmazione multi-stadio, rispondono alla necessità di soluzioni per scopi generali che non paghino spese generali interpretative di runtime.

Poi passano a diversi riferimenti a lavori più vecchi che presumibilmente dimostrano che la messa in scena è efficace, il che suggerisce che il concetto sia ancora più vecchio. Non forniscono un riferimento per il termine stesso.

Queste affermazioni sembrano essere ortogonali, se non contraddittorie; forse ciò che scrivono Rompf e Odersky è un'applicazione di ciò che Taha e Sheard propongono, ma forse è un'altra prospettiva sulla stessa cosa. Sembrano concordare sul fatto che un punto importante sia che i programmi (ri) scrivono parti di se stessi in fase di esecuzione, ma non so se sia un'abilità necessaria e / o sufficiente.

Quindi, cosa è la stadiazione rispettivamente sono le interpretazioni della stadiazione in questo contesto? Da dove viene il termine?


  1. Staging modulare leggero: un approccio pragmatico alla generazione del codice di runtime e DSL compilati di T. Rompf e M. Odersky (2012)
  2. MetaML e programmazione multi-stage con annotazioni esplicite di W. Taha e T. Sheard (2000)

Quale contraddizione vedi tra le due affermazioni? A me sembra che stiano parlando della stessa cosa, con enfasi diversa.
Gilles 'SO- smetti di essere malvagio' il

@Gilles Non ho bisogno di generazione / compilazione di codice di runtime per ritardare la valutazione di qualcosa (vedi continuable). Può darsi che sia solo un'altra enfasi (ammetto quell'opzione nella domanda), ma non posso davvero dirlo.
Raffaello

È possibile controllare l'implementazione del linguaggio di programmazione Julia e la documentazione sulla metaprogrammazione su @generated functions: julia.readthedocs.org/en/latest/manual/metaprogramming/…
SalchiPapa,

Risposte:


21

Per quanto ne so , il termine calcolo graduale è stato usato per la prima volta da Bill Scherlis in questo documento . Prima di ciò, il termine " valutazione parziale " era usato per lo stesso concetto, ma l'idea del calcolo graduale è leggermente diversa. Entrambe le idee sono correlate al teorema di Smn di Kleene .

Se hai una funzione di due argomenti, ma conosci un argomento, ad esempio , puoi eseguire subito parte del calcolo della funzione usando la conoscenza del primo argomento. Ciò che ti rimane quindi è una funzione cui calcoli dipendono solo dal secondo argomento sconosciuto.m ϕ m ( n )ϕ(m,n)mϕm(n)

L'idea di valutazione parziale consiste nel calcolare la funzione specializzata automaticamente . Dato il codice per la funzione originale , la valutazione parziale fa un'analisi statica per determinare quali bit del codice dipendono da quali bit dipendono da , e lo trasforma in una funzione che, dato , costruisce . Il secondo argomento può quindi essere alimentato a questa funzione specializzata.ϕ m n ϕ m ϕ m nϕm(n) ϕmnϕmϕmn

L'idea del calcolo graduale è di pensare prima alla funzione . Si chiama funzione "messa in scena" perché funziona in più fasi. Una volta dato il primo argomento , costruisce il codice per la funzione specializzata . Questo è il "primo stadio". Nella seconda fase, il secondo argomento viene fornito a che svolge il resto del lavoro. m ϕ m ϕ mϕmϕmϕm

Quindi, il compito della valutazione parziale è trasformare il codice per una normale funzione in una funzione . Scherlis prevedeva che questa trasformazione potesse essere effettuata con meccanismi più generali rispetto ai precedenti metodi di valutazione parziale. L'argomento del "calcolo graduale" ora tratta temi come:ϕ ϕϕ

  • Come definire le funzioni in scena?
  • Quali linguaggi di programmazione e sistemi di tipi dovrebbero essere usati per definire le funzioni in scena?
  • Qual è la semantica di tali lingue?
  • Come garantiamo la coerenza e la correttezza delle funzioni messe in scena?
  • Quali tecniche sono utili per costruire automaticamente o semiautomaticamente funzioni in scena?
  • Come dimostriamo la correttezza di tali tecniche?

Il calcolo a fasi può essere molto importante nella pratica. In effetti, ogni compilatore è in effetti un calcolo a fasi. Dato un programma sorgente, costruisce un programma target tradotto e ottimizzato, che può quindi prendere l'input effettivo e calcolare il risultato. In pratica è difficile scrivere programmi di calcolo graduale perché dobbiamo destreggiarci tra le varie fasi e assicurarci che le cose giuste vengano fatte al momento giusto. Chiunque abbia scritto un compilatore ha lottato con tali problemi. È anche difficile scrivere programmi che scrivano altri programmi, siano essi programmi in linguaggio macchina (compilatori), query SQL (manipolazioni di database) o codice HTML / Server Pages / Javascript (applicazioni web) e miriadi di altre applicazioni.


Per quanto posso vedere, quindi la differenza tra il calcolo graduale e la valutazione parziale è la forma di ? (c'è qualche che può essere ottenuto dal calcolo graduale ma non dalla valutazione parziale). ϕ ϕϕ
Ta Thanh Dinh,

Quindi, ciò che intendi per valutazione parziale è un'astrazione rispetto alla programmazione multi-stage, il che significa che la valutazione parziale non implica una programmazione multi-fase ma la programmazione multi-fase implica una valutazione parziale. Per cui è possibile eseguire una valutazione parziale in uno o più stadi poiché il curry in linguaggi funzionali non implica necessariamente più stadi e la generazione di codice in fase di esecuzione, giusto?
denis631,

1
Non esattamente. Un valutatore parziale compila un normale programma in un programma a 2 fasi e quindi esegue la sua prima fase. Nella programmazione graduale, scrivi tu stesso il programma multi-stage.
Uday Reddy,

9

Sebbene le altre risposte siano tecnicamente corrette, non credo che forniscano una comprensione corretta del perché gli scienziati informatici siano interessati alle funzioni in scena.

Creando funzioni a fasi, si definiscono i programmi che generano programmi. Uno dei grandi obiettivi della moderna teoria del linguaggio pratico è massimizzare il potenziale riutilizzo. Vogliamo rendere possibile la scrittura di librerie che non sono solo funzioni e oggetti utili, ma che aiutano i programmatori fornendo costruzioni architettoniche di ordine superiore.

Sarebbe bello se potessimo sbarazzarci di tutto il codice del boilerplate. Dovremmo essere in grado di ridurre al minimo il linguaggio delle specifiche. Se desideriamo un dispatcher guidato da eventi, ad esempio, che comunica con altri dispatcher con una determinata progettazione di thread, dovremmo essere in grado di specificarlo in modo compatto, e tutti i listener IO e le connessioni di oggetti in coda e thread dovrebbero poter essere costruiti da quella specifica.

Le lingue di dominio tendono ad essere quelle rappresentazioni compatte che stiamo cercando. Quando le persone lavorano in un dominio per un po ', la lingua che usano tende a eliminare la maggior parte delle duplicazioni di informazioni e diventa una specifica snella. Quindi questa teoria della messa in scena tende a diventare un sistema di traduzione dalle lingue di dominio alla lingua di esecuzione.

I compilatori sono tecnicamente stagisti, ma manca l'obiettivo. L'obiettivo della stadiazione moderna è quello di consentire la creazione di programmi che costruiscono programmi per massimizzare il riutilizzo e automatizzare la costruzione del programma laddove possibile. Sarebbe bello se un giorno i requisiti funzionali di un programma fossero il programma.

Vedi "Programmazione generativa" di Czarnecki ed Eisenecker (ISBN-13: 978-0201309775).


@Raphael: ecco il capitolo tre con le basi sui domini e il riutilizzo. Guarda anche l'ottimizzazione di cui parli. La FFT non viene eseguita dalla stadiazione per renderla più veloce. È fatto in modo che il programmatore non debba calcolare manualmente la tabella dei valori ogni volta, copiarli nel programma e creare un grande elenco. È per ridurre al minimo il lavoro svolto e riutilizzare i passaggi di base. Lo stesso con lo svolgimento di loop. Farlo a mano ripete le informazioni e non può essere riutilizzato.
ex0du5,

Questo punto di vista DSL sembra limitare la messa in scena a un livello (un compilatore DSL all'interno del programma), giusto?
Raffaello

1
@Raphael: dipende davvero dal tuo punto di vista. Ovviamente, il concetto non aggiunge alcun potere computazionale se visto semplicemente come la fonte -> traduzione eseguibile. Potremmo semplicemente creare un compilatore per il linguaggio DS ed essere fatto. Da dove viene la sua forza è nell'iterazione. Quando si creano librerie che verranno utilizzate e ampliate dai progetti in futuro, vengono visualizzate fasi naturali all'interno dei confini della biblioteca. Potresti avere una libreria che trasforma le specifiche degli oggetti in sorgente per la serializzazione completa, e quindi un'altra libreria che costruisce il livello di trasporto basato su alcune specifiche di invio ...
ex0du5

1
@Raphael: la messa in scena può essere più naturale con più fasi. Se nel corso di un pezzo di codice i suoi requisiti cambiano molto nel tempo, laddove altri sono molto più stabili, potrebbe essere appropriato a causa di "tranciare i livelli" per separare la messa in scena in livelli con interfacce più stabili. È quindi possibile influenzare meno del sistema con le modifiche e rispettare una forma di messa in scena del principio aperto-chiuso. Quelle sono preoccupazioni pratiche che non hanno necessità matematiche, ma è tutto basato sulla praticità. Non vogliamo un unico linguaggio di compilazione, vogliamo consentire l'evoluzione.
ex0du5,

5

La risposta è data nel pezzo di prospettiva tecnica per l'articolo in questione [1]. Il problema in esame è l'area di tensione tra codice generale e codice specifico:

I programmi possono essere scritti per scopi generici o speciali. Il codice per scopi generici ha il vantaggio di essere utilizzabile in una varietà di situazioni, mentre il codice per scopi speciali potrebbe essere scritto in un modo che sfrutta le caratteristiche uniche dell'ambiente di esecuzione e quindi guadagna efficienza a costo di riusabilità.

Ovviamente vogliamo risolvere questa tensione, ovvero raggiungere il codice generale e l' implementazione specifica:

Possiamo porre la domanda: è possibile scrivere codice in modo che sia di uso generale, ma poi si specializzi automaticamente sulla situazione attuale durante l'esecuzione?

Ciò ha dato origine all'idea di avere programmi (generali) di (ri) scrivere se stessi in fase di esecuzione per adattarsi a una situazione specifica:

Di conseguenza, un'importante direzione della ricerca ha coinvolto la ricerca di linguaggio e tecnologia di compilazione che può consentire ai programmatori di scrivere codice generico che viene poi correttamente ed efficientemente trasformato in codice specializzato ad alte prestazioni in fase di esecuzione.

Immagino che JIT di Java sia un buon esempio. Un'idea particolare è la programmazione in più fasi, che Lee spiega in questo modo:

In questa linea di ricerca, una delle idee fondamentali è il concetto di messa in scena. Immaginiamo che l'esecuzione di un programma proceda in una serie di fasi, ognuna calcolando i valori usati dalle fasi successive. Ciò che cerchiamo, quindi, è scrivere il codice del programma in modo che in qualche modo queste fasi siano rese evidenti. Se ciò viene fatto, allora possiamo organizzare che il codice della fase successiva sia compilato in generatori di codice che ottimizzano rispetto ai risultati dei calcoli della fase precedente.

Cioè, la "stadiazione" è un modo di guardare funzioni / codice adeguati che identifica le fasi nel calcolo / esecuzione che possono essere semplificate conoscendo i risultati delle fasi precedenti. Il calcolo "Ritardare" come nella prima citazione nella domanda può essere un effetto collaterale necessario per separare correttamente le fasi, ma non è questo il punto.

Rompf e Odersky menzionano la trasformata veloce di Fourier come esempio che può essere istruttivo.


  1. La volpe e il riccio: prospettiva tecnica di Peter Lee (2012)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.