Esistono molte lingue che consentono una sorta di metaprogrammazione . In particolare, sono sorpreso di non avere risposta parlando della famiglia di lingue Lisp .
Da Wikipedia:
La metaprogrammazione è la scrittura di programmi per computer con la capacità di trattare i programmi come i loro dati.
Più avanti nel testo:
Lisp è probabilmente il linguaggio per antonomasia con strutture di metaprogrammazione, sia per la sua precedenza storica sia per la semplicità e la potenza della sua metaprogrammazione.
Lingue Lisp
Segue una breve introduzione a Lisp.
Un modo per vedere il codice è come una serie di istruzioni: fai questo, poi fallo, poi fai quest'altra cosa ... Questa è una lista! Un elenco di cose da fare per il programma. E ovviamente puoi avere liste all'interno di liste per rappresentare loop e così via.
Se rappresentiamo una lista contenente gli elementi a, b, c, d come questo: (abcd) otteniamo qualcosa che assomiglia a una chiamata di funzione Lisp, in cui a
è la funzione, e b
, c
, d
sono gli argomenti. In realtà il tipico "Hello World!" il programma potrebbe essere scritto in questo modo:(println "Hello World!")
Certo b
, c
o d
potrebbero essere elenchi che valutano anche qualcosa. Quanto segue: (println "I can add :" (+ 1 3) )
stampa quindi "" Posso aggiungere: 4 ".
Quindi, un programma è una serie di elenchi nidificati e il primo elemento è una funzione. La buona notizia è che possiamo manipolare le liste! Quindi possiamo manipolare i linguaggi di programmazione.
Il vantaggio di Lisp
Lisps non è tanto un linguaggio di programmazione quanto un kit di strumenti per creare linguaggi di programmazione. Un linguaggio di programmazione programmabile.
Non è molto più semplice creare nuovi operatori in Lisps, ma è anche quasi impossibile scrivere alcuni operatori in altre lingue perché gli argomenti vengono valutati quando passati alla funzione.
Ad esempio in un linguaggio di tipo C diciamo che vuoi scrivere if
tu stesso un operatore, qualcosa del tipo:
my-if(condition, if-true, if-false)
my-if(false, print("I should not be printed"), print("I should be printed"))
In questo caso entrambi gli argomenti verranno valutati e stampati, in un ordine che dipende dall'ordine di valutazione degli argomenti.
In Lisps, scrivere un operatore (lo chiamiamo macro) e scrivere una funzione è più o meno la stessa cosa e viene usato allo stesso modo. La differenza principale è che i parametri di una macro non vengono valutati prima di essere passati come argomenti alla macro. Questo è essenziale per poter scrivere alcuni operatori, come if
sopra.
Lingue del mondo reale
Mostrando come sia esattamente un po 'fuori portata qui, ma ti incoraggio a provare a programmare in un Lisp per saperne di più. Ad esempio potresti dare un'occhiata a:
- Schema , un vecchio, abbastanza "puro" Lisp con un piccolo nucleo
- Common Lisp, un Lisp più grande con un sistema di oggetti ben integrato e molte implementazioni (è standardizzato ANSI)
- Racchetta un Lisp digitato
- Clojure il mio preferito, gli esempi sopra erano il codice Clojure. Un moderno Lisp in esecuzione sulla JVM. Ci sono alcuni esempi di macro Clojure anche su SO (ma questo non è il punto giusto da cui iniziare. Guarderei inizialmente koans 4clojure , braveclojure o clojure )).
Oh, a proposito, Lisp significa LISt Processing.
Per quanto riguarda i tuoi esempi
Ho intenzione di dare esempi usando Clojure di seguito:
Se riesci a scrivere una add
funzione in Clojure (defn add [a b] ...your-implementation-here... )
, puoi chiamarla +
così (defn + [a b] ...your-implementation-here... )
. Questo è in effetti ciò che viene fatto nella vera implementazione (il corpo della funzione è un po 'più coinvolto ma la definizione è essenzialmente la stessa di quella che ho scritto sopra).
Che dire della notazione infix? Bene Clojure usa una prefix
notazione (o polacca), quindi potremmo creare una infix-to-prefix
macro che trasformerebbe il codice con prefisso in codice Clojure. Il che è sorprendentemente semplice (in realtà è uno degli esercizi macro nei koan clojure)! Può anche essere visto in natura, ad esempio vedi macro Incanter$=
.
Ecco la versione più semplice dei koan spiegati:
(defmacro infix [form]
(list (second form) (first form) (nth form 2)))
;; takes a form (ie. some code) as parameter
;; and returns a list (ie. some other code)
;; where the first element is the second element from the original form
;; and the second element is the first element from the original form
;; and the third element is the third element from the original form (indexes start at 0)
;; example :
;; (infix (9 + 1))
;; will become (+ 9 1) which is valid Clojure code and will be executed to give 10 as a result
Per guidare ulteriormente il punto, alcune citazioni di Lisp :
“Parte di ciò che distingue Lisp è che è progettato per evolversi. Puoi usare Lisp per definire nuovi operatori Lisp. Man mano che nuove astrazioni diventano popolari (programmazione orientata agli oggetti, ad esempio), risulta sempre facile implementarle in Lisp. Come il DNA, un linguaggio del genere non passa di moda. "
- Paul Graham, ANSI Common Lisp
“Programmare in Lisp è come giocare con le forze primordiali dell'universo. Sembra un lampo tra le punte delle dita. Nessun'altra lingua si sente nemmeno vicina. "
- Glenn Ehrlich, Road to Lisp