Linguaggi di programmazione con un meccanismo di estensione della sintassi simile a Lisp [chiuso]


20

Ho solo una conoscenza limitata di Lisp (cercando di imparare un po 'nel mio tempo libero) ma per quanto ho capito le macro Lisp permettono di introdurre nuovi costrutti di linguaggio e sintassi descrivendoli in Lisp stesso. Ciò significa che un nuovo costrutto può essere aggiunto come libreria, senza modificare il compilatore / interprete Lisp.

Questo approccio è molto diverso da quello di altri linguaggi di programmazione. Ad esempio, se volessi estendere Pascal con un nuovo tipo di loop o un linguaggio particolare, dovrei estendere la sintassi e la semantica del linguaggio e quindi implementare quella nuova funzionalità nel compilatore.

Esistono altri linguaggi di programmazione al di fuori della famiglia Lisp (ad eccezione di Common Lisp, Scheme, Clojure (?), Racket (?), Ecc.) Che offrono un'analoga possibilità di estendere la lingua all'interno della lingua stessa?

MODIFICARE

Per favore, evita una discussione estesa e sii specifico nelle tue risposte. Invece di un lungo elenco di linguaggi di programmazione che possono essere estesi in un modo o nell'altro, vorrei capire da un punto di vista concettuale ciò che è specifico per le macro Lisp come meccanismo di estensione e quali linguaggi di programmazione non Lisp offrono alcuni concetti quello è vicino a loro.


6
Lisp ha un altro trucco oltre alle normali macro. "Reader Macro" consente di catturare ed estendere la sintassi del parser in fase di esecuzione, quindi anche la struttura di token di base della lingua è sotto controllo.
Ddyer,

@ddyer: Grazie, non lo sapevo: un altro argomento da aggiungere al mio elenco di letture.
Giorgio,

Scommetto che la metaprogrammazione di Ruby può soddisfare questo.
Rig

1
la tua domanda è contraddittoria, prima chiedi un elenco, poi chiedi informazioni concettuali, che cos'è ???

Sto chiedendo un elenco, ma non generico (non solo un linguaggio di programmazione che può essere esteso in qualche modo) perché sarebbe troppo generico. Mi piacerebbe conoscere le lingue che possono essere estese in modo simile a Lisp (l'estensione è definita usando la stessa lingua che estende, non una sorta di meta-lingua). Le risposte di Péter Török, Thomas Eding, SK-logic e Mechanical snail sembrano essere le più vicine a ciò che avevo in mente. Devo ancora leggere attentamente tutte le risposte.
Giorgio,

Risposte:


19

Anche Scala lo rende possibile (in effetti è stato progettato consapevolmente per supportare la definizione di nuovi costrutti linguistici e persino DSL completi).

Oltre alle funzioni di ordine superiore, lambda e curry, che sono comuni nei linguaggi funzionali, ci sono alcune caratteristiche linguistiche speciali qui per abilitare questo *:

  • nessun operatore: tutto è una funzione, ma i nomi delle funzioni possono includere caratteri speciali come '+', '-' o ':'
  • i punti e le parentesi graffe possono essere omessi per le chiamate di metodo a parametro singolo, ovvero a.and(b)equivalgono a a and bin forma di infisso
  • per le chiamate di funzioni a parametro singolo è possibile utilizzare parentesi graffe anziché parentesi graffe normali: questo (insieme al curry) consente di scrivere cose come

    val file = new File("example.txt")
    
    withPrintWriter(file) {
      writer => writer.println("this line is a function call parameter")
    }
    

    dove withPrintWriterè un metodo semplice con due elenchi di parametri, entrambi contenenti un singolo parametro

  • i parametri per nome consentono di omettere un elenco di parametri vuoto in lambdas, consentendo di scrivere chiamate come myAssert(() => x > 3)in una forma più breve comemyAssert(x > 3)

La creazione di un DSL di esempio è discussa in dettaglio nel capitolo 11. Linguaggi specifici di dominio in Scala del libro gratuito Programming Scala .

* Non intendo che questi siano unici per Scala, ma almeno non sembrano essere molto comuni. Tuttavia non sono un esperto di linguaggi funzionali.


1
+1: interessante. Questo significa che per estendere la sintassi posso definire nuove classi e che le firme del metodo forniranno la nuova sintassi come sottoprodotto?
Giorgio,

@Giorgio, praticamente sì.
Péter Török,

I collegamenti non funzionano più.
Nate Glenn,

13

Perl consente la preelaborazione della sua lingua. Anche se questo non è spesso usato nella misura in cui cambia radicalmente la sintassi nella lingua, può essere visto in alcuni dei ... moduli dispari:

  • Acme :: Bleach per un codice davvero pulito
  • Acme :: Morse per la scrittura in codice morse
  • Lingua :: Romana :: Perligata per la scrittura in latino (ad esempio, i sostantivi sono variabili e il numero, il caso e la declinazione cambiano il tipo del nome nextum ==> $ next, nexta ==> @next)

Esiste anche un modulo che consente a perl di eseguire codice che sembra scritto in Python.

Un approccio più moderno a questo all'interno di perl sarebbe l'uso di Filter :: Simple (uno dei moduli principali in perl5).

Si noti che tutti questi esempi riguardano Damian Conway che è stato definito il "Dottore pazzo del Perl". È ancora un'abilità incredibilmente potente all'interno del perl di distorcere il linguaggio come lo si desidera.

Perlfilter esiste più documentazione per questa e altre alternative .


13

Haskell

Haskell ha "Template Haskell" e "Quasiquotation":

http://www.haskell.org/haskellwiki/Template_Haskell

http://www.haskell.org/haskellwiki/Quasiquotation

Queste funzionalità consentono agli utenti di aggiungere drammaticamente alla sintassi della lingua al di fuori dei mezzi normali. Questi vengono risolti anche in fase di compilazione, che ritengo sia un must (almeno per i linguaggi compilati) [1].

In passato ho usato quasiquotation in Haskell per creare un pattern matcher avanzato su un linguaggio di tipo C:

moveSegment :: [Token] -> Maybe (SegPath, SegPath, [Token])
moveSegment [hc| HC_Move_Segment(@s, @s); | s1 s2 ts |] = Just (mkPath s1, mkPath s2, ts)
moveSegment _ = Nothing

[1] Altrimenti il ​​seguente si qualifica come estensione di sintassi:, runFeature "some complicated grammar enclosed in a string to be evaluated at runtime"che ovviamente è un sacco di schifezze.


3
Esistono anche altre funzionalità di Haskell che consentono essenzialmente la sintassi personalizzata, come la creazione del proprio operatore o il curry automatico abbinato alle funzioni lambda (si consideri ad esempio l'uso tipico di forM).
Xion,

Onestamente penso che gli operatori di curry e personalizzati non siano idonei. Consentono di utilizzare la lingua in modo ordinato, ma non ti consentono di aggiungere / new / funzionalità alla lingua. TH e QQ lo fanno. In senso stretto, né TH e QQ nel senso che fanno esattamente quello per cui sono progettati, ma ti permettono di uscire davvero "dalla lingua" al momento della compilazione.
Thomas Eding,

1
"Altrimenti il ​​seguente si qualifica come estensione di sintassi ...": ottimo punto.
Giorgio,

12

Tcl ha una lunga storia di supporto per la sintassi estensibile. Ad esempio, ecco l'implementazione di un ciclo che esegue l'iterazione di tre variabili (fino all'arresto) sui cardinali, i loro quadrati e i loro cubi:

proc loopCard23 {cardinalVar squareVar cubeVar body} {
    upvar 1 $cardinalVar cardinal $squareVar square $cubeVar cube

    # We borrow a 'for' loop for the implementation...
    for {set cardinal 0} true {incr cardinal} {
        set square [expr {$cardinal ** 2}]
        set cube [expr {$cardinal ** 3}]

        uplevel 1 $body
    }
}

Sarebbe quindi usato in questo modo:

loopCard23 a b c {
    puts "got triplet: $a, $b, $c"
    if {$c > 400} {
        break
    }
}

Questo tipo di tecnica è ampiamente utilizzata nella programmazione di Tcl, e la chiave per farlo in modo sano sono i comandi upvare uplevel( upvarlega una variabile denominata in un altro ambito a una variabile locale ed uplevelesegue uno script in un altro ambito: in entrambi i casi, 1indica che lo scopo in questione è quello del chiamante). È anche usato molto nel codice che si accoppia con i database (eseguendo un po 'di codice per ogni riga in un set di risultati), in Tk per GUI (per eseguire il binding dei callback agli eventi), ecc.

Tuttavia, questa è solo una parte di ciò che viene fatto. Il linguaggio incorporato non deve nemmeno essere Tcl; può essere praticamente qualsiasi cosa (fintanto che equilibra le parentesi graffe - le cose diventano sintatticamente orribili se ciò non è vero - che è la stragrande maggioranza dei programmi) e Tcl può semplicemente inviare alla lingua straniera incorporata, se necessario. Esempi di ciò includono l' incorporamento di C per implementare i comandi Tcl e l'equivalente con Fortran. (Probabilmente, tutti i comandi integrati di Tcl vengono eseguiti in questo modo in un certo senso, in quanto sono in realtà solo una libreria standard e non il linguaggio stesso.)


10

Questa è in parte una questione di semantica. L'idea di base di Lisp è che il programma è costituito da dati che possono essere manipolati. Le lingue comunemente usate nella famiglia Lisp, come Scheme, non ti permettono davvero di aggiungere una nuova sintassi in senso parser; sono solo elenchi tra parentesi delimitati da spazi. È solo che poiché la sintassi principale fa così poco, puoi ricavarne quasi tutti i costrutti semantici . Scala (discusso di seguito) è simile: le regole dei nomi delle variabili sono così liberali che puoi facilmente ricavarne delle belle DSL (rimanendo all'interno delle stesse regole di sintassi di base).

Questi linguaggi, sebbene in realtà non ti permettano di definire una nuova sintassi nel senso dei filtri Perl, hanno un nucleo sufficientemente flessibile che puoi usarlo per costruire DSL e aggiungere costrutti linguistici.

L'importante caratteristica comune è che ti permettono di definire costrutti linguistici che funzionano così come quelli incorporati, usando le funzioni esposte dalle lingue. Il grado di supporto per questa funzione varia:

  • Molte lingue più anziani a condizione built-in funzioni come sin(), round()e così via, senza alcun modo per implementare il proprio.
  • C ++ offre supporto limitato. Ad esempio, alcuni built-in parole chiave come calchi ( static_cast<target_type>(input), dynamic_cast<>(), const_cast<>(), reinterpret_cast<>()) possono essere emulato utilizzando le funzioni del modello, che utilizza Boost per lexical_cast<>(), polymorphic_cast<>(), any_cast<>(), ....
  • Java è dotato di strutture di controllo ( for(;;){}, while(){}, if(){}else{}, do{}while(), synchronized(){}, strictfp{}) e non consente di definire il proprio. Scala invece definisce una sintassi astratta che ti consente di chiamare le funzioni usando una comoda sintassi simile a una struttura di controllo, e le biblioteche la usano per definire efficacemente nuove strutture di controllo (ad es. react{}Nella libreria degli attori).

Inoltre, è possibile esaminare la funzionalità di sintassi personalizzata di Mathematica nel pacchetto Notazione . (Tecnicamente appartiene alla famiglia Lisp, ma ha alcune caratteristiche di estensibilità fatte diversamente, così come la solita estensibilità Lisp.)


2
Sbagliato. Lisp fa in realtà consente di aggiungere assolutamente qualsiasi tipo di una nuova sintassi. Come in questo esempio: meta-alternative.net/pfront.pdf - non è altro che una macro Lisp.
SK-logic,

Ciò sembra essere implementato in un linguaggio appositamente progettato per la creazione di DSL . Naturalmente è possibile creare una lingua nella famiglia Lisp che fornisce anche tali funzionalità; Intendevo dire che quella funzione non è un'idea base di Lisp supportata da Lisps usati in modo comune (ad es. Schema). A cura di chiarire.
Lumaca meccanica,

Questo "linguaggio per la creazione di DSL" è una raccolta di macro costruita su un Lisp piuttosto tipico e minimalista. Può essere facilmente portato su qualsiasi altro Lisp con (defmacro ...). Attualmente sto trasferendo questa lingua su Racket, solo per divertimento. Ma sono d'accordo sul fatto che non è molto utile, poiché la sintassi delle espressioni S è più che sufficiente per la maggior parte delle possibili semantiche utili.
SK-logic,

E, Scheme non è diverso dal Common Lisp, a partire da R6RS ufficialmente e ufficiosamente per secoli, poiché fornisce un (define-macro ...)equivalente, che, a sua volta, può utilizzare qualsiasi tipo di analisi internamente.
SK-logic,

8

Rebol sembra quasi quello che stai descrivendo, ma un po 'di lato.

Invece di definire una sintassi specifica, tutto in Rebol è una chiamata di funzione - non ci sono parole chiave. (Sì, puoi ridefinire ife whilese lo desideri davvero). Ad esempio, questa è una ifdichiarazione:

if now/time < 12:00 [print "Morning"]

ifè una funzione che accetta 2 argomenti: una condizione e un blocco. Se la condizione è vera, il blocco viene valutato. Sembra la maggior parte delle lingue, giusto? Bene, il blocco è una struttura di dati, non è limitato al codice - questo è un blocco di blocchi, ad esempio, e un rapido esempio della flessibilità di "codice è dato":

SomeArray: [ [foo "One"] [bar "Two"] [baz "Three"] ]
foreach action SomeArray [action/1: 'print] ; Change the data
if now/time < 12:00 SomeArray/2 ; Use the data as code - right now, if now/time < 12:00 [print "Two"]

Fintanto che è possibile attenersi alle regole della sintassi, l'estensione di questo linguaggio non sarà altro che la definizione di nuove funzioni. Ad esempio, alcuni utenti hanno eseguito il backport delle funzionalità di Rebol 3 in Rebol 2.


7

Ruby ha una sintassi abbastanza flessibile, penso che sia un modo di "estendere la lingua all'interno della lingua stessa".

Un esempio è il rake . È scritto in Ruby, è Ruby, ma sembra make .

Per verificare alcune possibilità è possibile cercare le parole chiave Ruby e metaprogrammazione .


13
La "sintassi flessibile" è MOLTO diversa dalla "sintassi estensibile". È passato molto tempo da quando ho programmato in Ruby, ma Rake sembra solo un uso ben progettato della sintassi integrata. In altre parole, questo non è un esempio.
Thomas Eding,

2
Non è una questione di laurea, non di tipo? Come distingueresti tra un linguaggio "sintassi estensibile" che può estendere alcuni aspetti della sua sintassi ma non altri, e un linguaggio "sintassi flessibile"?
comingstorm

1
Se la linea è sfocata, allora spingiamo indietro la linea in modo che C sia considerato per supportare la sintassi estensibile.
Thomas Eding,

Per creare un collegamento con il tuo esempio, vorrei tracciare la linea qui: un linguaggio con sintassi estensibile può creare rake, che sembra make. Una lingua con sintassi flessibile può essere estesa (ah, bella fusione linguistica lì) per compilare ed eseguire makefile. Il tuo punto in materia di laurea è comunque buono. Forse alcune lingue consentono di compilare Make, ma non Python. E altri consentirebbero entrambi.
Clayton Stanley,

Qualsiasi linguaggio completo di turing dovrebbe essere in grado di essere elaborato per elaborare i file. La flessibilità della sintassi non è un fattore oltre quanto una sfida sarebbe.
Rig

7

L'estensione della sintassi nel modo in cui stai parlando ti consente di creare lingue specifiche del dominio. Quindi forse il modo più utile per riformulare la tua domanda è: quali altre lingue hanno un buon supporto per le lingue specifiche del dominio?

Ruby ha una sintassi molto flessibile e molti DSL sono fioriti lì, come il rake. Groovy include molta di quella bontà. Include anche trasformazioni AST, che sono più direttamente analoghe alle macro di Lisp.

R, il linguaggio per il calcolo statistico, consente alle funzioni di non sottovalutare i propri argomenti. Lo utilizza per creare un DSL per specificare la formula di regressione. Per esempio:

y ~ a + b

significa "adatta una linea della forma k0 + k1 * a + k2 * b ai valori in y".

y ~ a * b

significa "adatta una linea della forma k0 + k1 * a + k2 * b + k3 * a * b ai valori in y."

E così via.


2
Le trasformazioni AST di Groovy sono molto dettagliate rispetto alle macro Lisp o Clojure. Ad esempio l'esempio Groovy di 20+ righe su groovy.codehaus.org/Global+AST+Transformations potrebbe essere riscritto come una breve riga in Clojure, ad es. `(Questo (println ~ message)). Non solo, ma non dovresti nemmeno compilare il vaso, scrivere i metadati o qualsiasi altra cosa su quella pagina Groovy.
Vorg van Geir,

7

Convergere è un altro linguaggio di metaprogrammazione non lispy. E, in una certa misura, anche C ++ si qualifica.

Probabilmente, MetaOCaml è abbastanza lontano da Lisp. Per uno stile totalmente diverso di estensibilità della sintassi, ma tuttavia abbastanza potente, dai un'occhiata a CamlP4 .

Nemerle è un'altra lingua estensibile con una metaprogrammazione in stile Lisp, sebbene sia più vicina a lingue come la Scala.

E anche la Scala stessa diventerà presto anche una lingua del genere.

Modifica: ho dimenticato l'esempio più interessante: JetBrains MPS . Non è solo molto distante da qualsiasi cosa Lispish, è anche un sistema di programmazione non testuale, con un editor che opera direttamente a livello di AST.

Modifica2: per rispondere a una domanda aggiornata, non c'è nulla di unico ed eccezionale nelle macro Lisp. In teoria, qualsiasi linguaggio può fornire un tale meccanismo (l'ho anche fatto con la semplice C). Tutto ciò di cui hai bisogno è un accesso al tuo AST e una capacità di eseguire il codice in tempo di compilazione. Alcune riflessioni potrebbero essere utili (interrogazioni sui tipi, le definizioni esistenti, ecc.).


Il tuo link Scala dice esplicitamente che le macro proposte "non possono cambiare la sintassi di Scala". (Interessante, lo elenca come una differenza tra quella proposta e le macro del preprocessore C / C ++!)
ruakh,

@ruakh, sì, è lo stesso approccio di Converge e Template Haskell: l'applicazione macro è esplicitamente contrassegnata e non può essere mescolata con una sintassi "normale". Ma dentro puoi avere qualsiasi sintassi che ti piace, quindi probabilmente è una sintassi estensibile. Il requisito "non lisp", purtroppo, limita le tue opzioni a lingue come questa.
SK-logic,

"L'ho fatto anche con la semplice C": come è possibile?
Giorgio,

@Giorgio, ovviamente ho modificato un compilatore (aggiunte macro e compilazione di un modulo incrementale, che in realtà è abbastanza naturale per C).
SK-logic

Perché è necessario l'accesso all'AST?
Elliot Gorokhovsky,

6

Prolog consente di definire nuovi operatori che vengono tradotti in termini composti con lo stesso nome. Ad esempio, questo definisce un has_catoperatore e lo definisce come predicato per verificare se un elenco contiene l'atomo cat:

:- op(500, xf, has_cat).
X has_cat :- member(cat, X).

?- [apple, cat, orange] has_cat.
true ;
false.

Il xfmezzo che has_catè un operatore postfix; l'utilizzo fxlo renderebbe un operatore prefisso e xfxlo renderebbe un operatore infisso, prendendo due argomenti. Controllare questo collegamento per maggiori dettagli sulla definizione degli operatori in Prolog.


5

TeX manca totalmente dall'elenco. Lo sapete tutti, vero? Sembra qualcosa del genere:

Some {\it ``interesting''} example.

... tranne che è possibile ridefinire la sintassi senza restrizioni. Ad ogni (!) Token nella lingua può essere assegnato un nuovo significato. ConTeXt è un pacchetto macro che ha sostituito le parentesi graffe con parentesi quadre:

Some \it[``interesting''] example.

Il pacchetto macro più comune LaTeX ridefinisce anche la lingua per i suoi scopi, ad esempio aggiungendo la \begin{environment}…\end{environment}sintassi.

Ma non si ferma qui. Tecnicamente, puoi anche ridefinire i token per analizzare quanto segue:

Some <it>“interesting”</it> example.

Sì, assolutamente possibile. Alcuni pacchetti lo usano per definire linguaggi specifici per piccoli domini. Ad esempio, il pacchetto TikZ definisce una sintassi concisa per i disegni tecnici, che consente quanto segue:

\foreach \angle in {0, 30, ..., 330} 
  \draw[line width=1pt] (\angle:0.82cm) -- (\angle:1cm);

Inoltre, TeX è Turing completo, quindi puoi letteralmente fare tutto con esso. Non l'ho mai visto sfruttato appieno il suo potenziale perché sarebbe abbastanza inutile e molto contorto, ma è del tutto possibile rendere analizzabile il seguente codice semplicemente ridefinendo i token (ma questo probabilmente andrebbe ai limiti fisici del parser, a causa del modo in cui è costruito):

for word in [Some interesting example.]:
    if word == interesting:
        it(word)
    else:
        word

5

Boo ti consente di personalizzare fortemente la lingua in fase di compilazione attraverso le macro sintattiche.

Boo ha una "pipeline di compilatori estensibile". Ciò significa che il compilatore può chiamare il codice per eseguire trasformazioni AST in qualsiasi momento durante la pipeline del compilatore. Come sapete, cose come Generics di Java o Linq di C # sono solo trasformazioni di sintassi in fase di compilazione, quindi è abbastanza potente.

Rispetto a Lisp, il vantaggio principale è che funziona con qualsiasi tipo di sintassi. Boo sta usando una sintassi ispirata a Python, ma probabilmente potresti scrivere un compilatore estensibile con una sintassi C o Pascal. E poiché la macro viene valutata in fase di compilazione, non è prevista alcuna penalità per le prestazioni.

Gli aspetti negativi, rispetto a Lisp, sono:

  • Lavorare con un AST non è elegante come lavorare con le espressioni s
  • Poiché la macro viene richiamata in fase di compilazione, non ha accesso ai dati di runtime.

Ad esempio, ecco come è possibile implementare una nuova struttura di controllo:

macro repeatLines(repeatCount as int, lines as string*):
    for line in lines:
        yield [| print $line * $repeatCount |]

utilizzo:

repeatLines 2, "foo", "bar"

che viene tradotto, in fase di compilazione, in qualcosa del tipo:

print "foo" * 2
print "bar" * 2

(Sfortunatamente, la documentazione online di Boo è sempre irrimediabilmente obsoleta e non copre nemmeno materiale avanzato come questo. La migliore documentazione per la lingua che conosco è questo libro: http://www.manning.com/rahien/ )


1
La documentazione web per questa funzione è attualmente rotta e non scrivo Boo da solo, ma ho pensato che sarebbe stato un peccato se fosse stata trascurata. Apprezzo il feedback mod e riconsidererò come posso contribuire con informazioni gratuite nel mio tempo libero.
Dan,

4

La valutazione Mathematica si basa sulla corrispondenza e la sostituzione del modello. Ciò consente di creare le proprie strutture di controllo, modificare le strutture di controllo esistenti o modificare il modo in cui vengono valutate le espressioni. Ad esempio, potresti implementare la "logica fuzzy" in questo modo (un po 'semplificata):

fuzzy[a_ && b_]      := Min[fuzzy[a], fuzzy[b]]
fuzzy[a_ || b_]      := Max[fuzzy[a], fuzzy[b]]
fuzzy[!a_]           := 1-fuzzy[a]
If[fuzzy[a_], b_,c_] := fuzzy[a] * fuzzy[b] + fuzzy[!a] * fuzzy[c]

Questo sostituisce la valutazione per gli operatori logici predefiniti &&, || ,! e la Ifclausola integrata .

Puoi leggere queste definizioni come definizioni di funzioni, ma il vero significato è: se un'espressione corrisponde allo schema descritto sul lato sinistro, viene sostituita con l'espressione sul lato destro. È possibile definire la propria clausola If in questo modo:

myIf[True, then_, else_] := then
myIf[False, then_, else_] := else
SetAttributes[myIf, HoldRest]

SetAttributes[..., HoldRest] dice al valutatore che dovrebbe valutare il primo argomento prima della corrispondenza del modello, ma mantenere la valutazione per il resto fino a dopo che il modello è stato abbinato e sostituito.

Questo è ampiamente utilizzato all'interno delle librerie standard di Mathematica per definire, ad esempio, una funzione Dche prende un'espressione e valuta la sua derivata simbolica.


3

Metalua è un linguaggio e un compilatore compatibile con Lua che fornisce questo.

  • Piena compatibilità con fonti e bytecode Lua 5.1: semantica e sintassi pulite ed eleganti, incredibile potenza espressiva, buone prestazioni, portabilità quasi universale. - Un sistema macro completo, simile per potenza a quello che viene offerto dai dialetti di Lisp o Template Haskell; i programmi manipolati possono essere visti
    come codice sorgente, come alberi di sintassi astratti o come un
    loro mix arbitrario , a seconda di quale si adatti meglio al proprio compito.
  • Un parser estensibile dinamicamente, che ti consente di supportare le tue macro con una sintassi che si fonde perfettamente con il resto della lingua.

  • Un insieme di estensioni del linguaggio, tutte implementate come macro metalua regolari.

Differenze con Lisp:

  • Non disturbare gli sviluppatori con le macro quando non ne scrivono una: la sintassi e la semantica del linguaggio dovrebbero essere più adatte per il 95% delle volte in cui non stiamo scrivendo macro.
  • Incoraggia gli sviluppatori a seguire le convenzioni del linguaggio: non solo con "best practice rants" che nessuno ascolta, ma offrendo un'API che semplifica la scrittura delle cose in Metalua Way. La leggibilità da parte dei colleghi sviluppatori è più importante e più difficile da ottenere rispetto alla leggibilità da parte dei compilatori, e per questo, avere un insieme comune di convenzioni rispettate aiuta molto.
  • Tuttavia, fornisce tutto il potere che uno è disposto a gestire. Né Lua né Metalua sono in schiavitù e disciplina obbligatoria, quindi se sai cosa stai facendo, la lingua non ti ostacolerà.
  • Rendi ovvio quando accade qualcosa di interessante: tutte le meta-operazioni avvengono tra + {...} e - {...} e restano visivamente fuori dal codice normale.

Un esempio di applicazione è l'implementazione del pattern matching ML-like.

Vedi anche: http://lua-users.org/wiki/MetaLua


Sebbene Lua non sia davvero un Lisp, il tuo elenco di "differenze" è abbastanza sospetto (l'unico elemento rilevante è l'ultimo). E, naturalmente, la comunità di Lisp non concorderebbe con un rant che non si dovrebbe scrivere / usare le macro il 95% delle volte - il modo Lisp si inclina verso qualcosa come il 95% delle volte usando e scrivendo DSL, oltre alle macro, di corso.
SK-logic,

2

Se stai cercando lingue estensibili, dovresti dare un'occhiata a Smalltalk.

In Smalltalk, l' unico modo per programmare è in realtà estendere la lingua. Non c'è differenza tra l'IDE, le librerie o la lingua stessa. Sono tutti così intrecciati che Smalltalk è spesso indicato come un ambiente piuttosto che come una lingua.

Non scrivi applicazioni autonome in Smalltalk, invece estendi l'ambiente linguistico.

Controlla http://www.world.st/ per una manciata di risorse e informazioni.

Vorrei raccomandare Pharo come dialetto per entrare nel mondo di Smalltalk: http://pharo-project.org

Spero che abbia aiutato!


1
Sembra interessante.
Thomas Eding,

1

Esistono strumenti che consentono di creare linguaggi personalizzati senza scrivere un intero compilatore da zero. Ad esempio c'è Spoofax , che è uno strumento di trasformazione del codice: inserisci la grammatica di input e le regole di trasformazione (scritte in modo dichiarativo di livello molto alto) e quindi puoi generare il codice sorgente Java (o altra lingua, se ti interessa abbastanza) da un linguaggio personalizzato progettato da te.

Quindi, sarebbe possibile prendere la grammatica del linguaggio X, definire la grammatica del linguaggio X '(X con le estensioni personalizzate) e la trasformazione X' → X e Spoofax genererà un compilatore X '→ X.

Attualmente, se ho capito bene, il miglior supporto è per Java, con il supporto C # in fase di sviluppo (o almeno così ho sentito). Questa tecnica potrebbe essere applicata a qualsiasi lingua con grammatica statica (quindi, probabilmente non Perl ).


1

Forth è un'altra lingua che è altamente estensibile. Molte implementazioni di Forth consistono in un piccolo kernel scritto in assembler o in C, quindi il resto del linguaggio è scritto in Forth stesso.

Esistono anche diversi linguaggi basati su stack ispirati a Forth e che condividono questa funzionalità, come Factor .


0

Funge-98

La funzione di impronte digitali di Funge-98 consente di eseguire una completa ristrutturazione dell'intera sintassi e semantica della lingua. Ma solo se l'implementatore fornisce un meccanismo di impronte digitali che ha permesso all'utente di modificare a livello di programmazione il linguaggio (ciò è teoricamente possibile implementare all'interno della normale sintassi e semantica Funge-98). In tal caso, si potrebbe letteralmente fare in modo che il resto del file (o qualunque parte del file) agisca come C ++ o Lisp (o qualunque cosa voglia).

http://quadium.net/funge/spec98.html#Fingerprints


perché l'hai pubblicato separatamente invece di aggiungerlo alla tua risposta precedente ?
moscerino

1
Perché Funge non ha nulla a che fare con Haskell.
Thomas Eding,

-1

Per ottenere ciò che stai cercando, hai davvero bisogno di quelle parentesi e di una mancanza di sintassi. Alcune lingue basate sulla sintassi potrebbero avvicinarsi, ma non è esattamente la stessa cosa di una vera macro.

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.