Il Rebol (o il Rosso) trarrebbe vantaggio dalle macro in stile Lisp?

7

Come sviluppatore di con una certa conoscenza del mondo esterno, sarei curioso dell'utilità / delle insidie nell'implementare macro in stile Lisp in Rebol (e / o Red) .

La mia comprensione (sempre felice di rivedere) è che Lisp è in grado di pre-elaborare il codice prima della valutazione / compilazione, modificando quel codice in base alle regole delle macro applicabili. Ciò può consentire affermazioni più complesse / espressive rispetto al contrario, con efficienze poiché tali istruzioni vengono espanse una sola volta quando viene caricato un corpo di codice. Le macro regole stesse sono relativamente semplici in quanto l'omoiconicity di Lisp consente di manipolare il codice con gli stessi termini con cui si modificherebbero i dati.

Poiché Rebol è anche un linguaggio omoiconico, è evidente che anch'esso potrebbe contenere macro tra caricamento e valutazione con grammatica di trasformazione espressamente similare (sebbene io ritenga che le più dichiarazioni di modello di valutazione di Rebol non generalmente vincolate da parentesi - lo renda più difficile identificare le parti del codice da trasformare).

La mia inclinazione è che, in generale, le macro non sono realmente necessarie in Rebol: puoi già creare strutture di codice complesse che sono abbastanza efficienti e, laddove è richiesta una maggiore efficienza, Rebol ha le capacità per adattarsi senza perdite significative di espressività. D'altra parte, 'equamente' e 'nessuna perdita significativa' potrebbero essere considerati donnole e le macro offrono un aumento dell'efficienza / espressività, ma a quale costo di implementazione?

Inoltre, come in Rebol è possibile caricare il codice prima della valutazione, sarebbe possibile implementare un meccanismo per load-expand-evaluation a livello di utente che consente implementazioni di macro di terze parti.

Questo post è in parte una risposta a questa domanda e tutto (più o meno) so di macro proviene da questo scambio .

Aggiornamento

Accesso al mondo delle macro

Red ha introdotto un rudimentale sistema di macro nella versione 0.6.2. Li ho usati in modo efficace per creare script cross-compatibili che hanno come target sia il Ren-C ramo di Rebol 3 e Red.

    
posta rgchris 08.07.2015 - 23:55
fonte

3 risposte

6

Questa è la prima volta che sento Rebol, ma da una rapida occhiata alla pagina di Wikipedia mi sembra che i dialetti Rebol siano proprio come le macro Lisp: entrambi ricevono il codice ordinario del linguaggio che ha superato lessicale e sintattico ( ma non semantico!) l'elaborazione, e l'elaborazione con le proprie regole semantiche.

Quindi, Rebol non trarrà vantaggio dalle macro Lisp, perché ha già quella caratteristica con un nome diverso, e avere due funzionalità di base che fanno esattamente la stessa cosa è pessima, specialmente per le lingue che si basano su Homoiconicity , dove la semplicità uniforme nella sintassi è la chiave della loro magia.

Aggiornamento

Lo spunto esplicativo nei commenti che le macro hanno il vantaggio di essere espanse una volta, prima dell'esecuzione del codice, e iniettate nel codice.

Questo, IMO, peggiorerà le cose, perché rovinerebbe l'ordine di parsing!

Ad esempio, supponiamo di avere il comando Rebol foo x bar y , dove foo è un dialetto e bar è una macro. Cosa dovrebbe fare l'interprete? Dipende dall'arit di foo :

  • Se foo accetta un singolo argomento, significa che abbiamo due comandi: foo x e bar y . Quindi bar y deve essere espanso.
  • Se foo accetta tre argomenti, significa che abbiamo un comando e bar è il secondo argomento di foo . Ciò significa che bar non deve essere espanso e invece passato a foo as-is.

Le regole sono chiare - per un umano, e probabilmente anche per i compilatori statici. Ma Rebol è dinamico e interpretato, quindi foo viene valutato solo quando deve essere eseguito - ma bar deve essere espanso prima che ciò accada! Quindi ora l'interpretato ha bisogno di valutare i dialetti prima che espanda le macro, in modo che possa espandere le macro prima di valutare il dialetto, e in tondo e in cerchio l'interpretazione va a cercare di risolvere il cerchio ...

Lisp non ha questo problema, perché:

  • È facile sapere da dove inizia una funzione / macro, perché è necessario metterli tra parentesi. Non puoi avere foo x bar y - devi avere (foo x bar y) o (foo x) (bar y) o anche (foo x (bar y)) , quindi è facile per l'interprete sapere cosa stai cercando di fare.
  • Lisp non ha dialetti, ma solo funzioni e macro. E poiché le funzioni non controllano la valutazione dei loro argomenti, i macro si valutano sempre da soli. Se Rebol non avesse dialetti e foo fosse una funzione, foo x bar y richiederebbe che l'interprete valuti prima x , bar e y , e poiché bar è una macro che avrebbe espanso indipendentemente dall'arità di foo , perché foo non ha alcun controllo sulla valutazione dei suoi argomenti.
risposta data 09.07.2015 - 02:49
fonte
3

Probabilmente Rebol non lo farebbe, dal momento che non esiste un passo di compilazione esplicito. Dovrebbe essere aggiunto un ulteriore passaggio "espansione macro" per implementare correttamente le macro Lisp, che non è poi così diverso dal suggerimento expand- * di @rgchris.

Le macro Lisp "funzionano" perché tutto ciò che entra in un'immagine Lisp è tecnicamente richiesto per passare attraverso un passo di compilazione dalla specifica Common Lisp. Quel passo è ciò che determina il risultato delle chiamate (eval-when), capisce come gestire i bit e i pezzi del flusso di controllo, fare JIT-ting e il codice di interesse generale magery. Il codice Lisp passa attraverso il lettore, tuttavia c'è sempre un'immagine esistente da richiamare. In un certo senso, il runtime e il compilatore Lisp funzionano in tandem. Al contrario di C, dove il compilatore funziona da solo. Rebol è simile a C in quell'aspetto; l'interprete è in esecuzione da solo. Hai bisogno di entrambi per le macro Lisp per avere un senso.

Poiché Red ha un JIT pianificato e porta avanti Red / System, che è sia JIT che compilato in anticipo, la possibilità di immergere macro Lisp sarebbe vantaggiosa. La maggior parte del lavoro svolto per supportare le macro è già fatto per necessità dai compilatori JIT e AOT, poiché devono già analizzare le funzioni e le firme dei parametri per determinare quale tipo di codice assembly verrà prodotto. Tutto ciò che rimane a questo punto è decidere se un'istruzione fa parte di un'istruzione da compilare o un'istruzione da eseguire nel contesto Red del compilatore e considerare il valore restituito come un'istruzione da compilare. Poiché l'esecuzione di eval è una pratica corrucciata in generale, questo significa che l'unica volta in cui una macro potrebbe potenzialmente cambiare lo stato dell'ambiente Red è durante le fasi di avvio o di sviluppo del programma: è già possibile modificare l'ambiente in esse, quindi aspettalo.

Lisp ha anche le funzioni (macroexpand-1) e (macroexpand-all), che ti permettono di vedere quale sarà l'ambiente Lisp come l'espansione di una determinata macro. Questo è sorprendentemente più utile per la debugabilità rispetto ai normali sistemi di template - puoi chiedere una copia esatta dell'espansione del template, che non è qualcosa che puoi chiedere a qualsiasi compilatore C ++. Lo dico perché qualcuno alla fine fa sempre notare come i sistemi macro possano rendere il debug un po 'più difficile; tuttavia, un sistema che è ancora più difficile eseguire il debug viene utilizzato su una lingua molto più popolare su base giornaliera.

Un esempio di questa utilità è che PARSE di Red può essere utilizzato per creare un dialetto che accetta la definizione di un formato di file binario, che quindi genera un blocco di codice Red / System, che alla fine verrà compilato. Poiché l'esecuzione è necessaria solo in tempo di compilazione , l'intero codice che esegue l'analisi, oltre al testo della specifica stessa, non è necessario nei binari. L'intero preprocessore potrebbe anche essere rimosso, poiché tutto, da #if a #define e #import, sarebbe ottenibile utilizzando un sistema macro simile a Lisp.

    
risposta data 04.10.2015 - 13:40
fonte
3

Probabilmente no.

Le macro Lisp sembrano avere due grandi scopi. Il primo è l'aspetto DRY ("don't-repeat-yourself"), che consente la cattura di schemi che altrimenti sarebbero difficili o impossibili nella lingua. Il secondo è un vantaggio prestazionale derivante dall'essere in grado di applicare un calcolo arbitrario in fase di compilazione.

Un buon numero di casi in cui i programmatori Lisp sembrano richiedere macro per espressioni è perché le normali funzioni Lisp sono limitate nel modo in cui possono lavorare con "frammenti di codice dipendenti dal contesto" passati come argomenti. Rebol attacca questo problema facendo legare i singoli simboli "viaggiano" con i loro frammenti di codice. Il risultato è che molti casi che richiedono macro in Lisp possono essere scritti come normali funzioni Rebol.

Inoltre, Rebol fa perno sulla sua storia "ASCIUTTA" sui dialetti. I dialetti sono essenzialmente l'idea di elaborare il codice "quotato" letteralmente sotto nuove regole, distinte dal valutatore principale. Questo è in concorrenza con i macro come strategia per l'estensione del linguaggio ... ed è anche incompatibile. Lo stesso Lisp non esegue macro in porzioni di codice citate e non ci sarebbe un modo generale di eseguire parametrizzazioni ed espansioni nella "grammatica" di un dialetto sconosciuto.

L'angolo di prestazione interessa solo il rosso (poiché è improbabile che Rebol sia mai compilato). Ma se i servizi in fase di compilazione sono disponibili per ottimizzare le cose a forma di funzione, allora l'unico vantaggio che le macro potrebbero offrire qui sarebbe per le cose che non potrebbero essere modellate come funzioni. Probabilmente i casi più motivanti troveranno una migliore espressione come un dialetto, che presumibilmente potrebbe anche trarre vantaggio dai servizi in fase di compilazione.

(Espansione in Rebol vs. Lisp Macros )

    
risposta data 20.04.2016 - 21:47
fonte

Leggi altre domande sui tag