Questa risposta è in risposta ai problemi sollevati da Illissius, punto per punto:
- È brutto da usare. $ (fooBar '' Asdf) non sembra carino. Superficiale, certo, ma contribuisce.
Sono d'accordo. Sento che $ () è stato scelto per sembrare che facesse parte del linguaggio, usando il familiare simbolo di Haskell. Tuttavia, questo è esattamente ciò che non vuoi / non vuoi nei simboli usati per la tua giunzione macro. Si fondono decisamente troppo, e questo aspetto cosmetico è abbastanza importante. Mi piace l'aspetto di {{}} per le giunzioni, perché sono visivamente distinti.
- Scrivere è ancora più brutto. La quotazione a volte funziona, ma per la maggior parte del tempo è necessario eseguire innesti e impianti idraulici AST manuali. L'API [1] è grande e ingombrante, ci sono sempre molti casi che non ti interessano ma che devono comunque essere spediti, e i casi che ti interessano tendono ad essere presenti in più forme simili ma non identiche (dati vs newtype, costruttori record rispetto a normali costruttori e così via). Scrivere è noioso e ripetitivo e abbastanza complicato da non essere meccanico. La [proposta di riforma] [2] affronta alcuni di questi aspetti (rendendo le citazioni più ampiamente applicabili).
Concordo anche con questo, tuttavia, come osservano alcuni dei commenti in "New Directions for TH", la mancanza di una buona quotazione AST pronta all'uso non è un difetto critico. In questo pacchetto WIP, cerco di risolvere questi problemi in formato libreria: https://github.com/mgsloan/quasi-extras . Finora consento lo splicing in alcuni punti in più rispetto al solito e riesco ad abbinare pattern su AST.
- La limitazione del palcoscenico è l'inferno. Non essere in grado di unire le funzioni definite nello stesso modulo è la parte più piccola di esso: l'altra conseguenza è che se si dispone di una giunzione di livello superiore, tutto ciò che segue nel modulo sarà fuori portata rispetto a qualsiasi cosa prima di esso. Altre lingue con questa proprietà (C, C ++) lo rendono fattibile consentendoti di inoltrare dichiarazioni, ma Haskell no. Se hai bisogno di riferimenti ciclici tra dichiarazioni congiunte o loro dipendenze e persone a carico, di solito sei semplicemente fregato.
Mi sono imbattuto nel problema che le definizioni cicliche di TH siano impossibili prima ... È abbastanza fastidioso. C'è una soluzione, ma è brutta: avvolgi le cose coinvolte nella dipendenza ciclica in un'espressione TH che combina tutte le dichiarazioni generate. Uno di questi generatori di dichiarazioni potrebbe essere solo un quasi-quoter che accetta il codice Haskell.
- Non ha principi. Ciò che intendo con questo è che la maggior parte delle volte quando esprimi un'astrazione, c'è una sorta di principio o concetto dietro quell'astrazione. Per molte astrazioni, il principio alla base può essere espresso nei loro tipi. Quando si definisce una classe di tipo, è spesso possibile formulare leggi a cui le istanze devono obbedire e che i clienti possono assumere. Se usi [la nuova funzione generics] [3] di GHC per astrarre la forma di una dichiarazione di istanza su qualsiasi tipo di dati (entro i limiti), puoi dire "per i tipi di somma, funziona così, per i tipi di prodotto, funziona così ". Ma Template Haskell è solo una stupida macro. Non è astrazione a livello di idee, ma astrazione a livello di AST, che è migliore, ma solo modestamente, dell'astrazione a livello di testo semplice.
È senza principi solo se fai cose senza principi con esso. L'unica differenza è che con il compilatore implementato meccanismi per l'astrazione, hai più fiducia che l'astrazione non abbia perdite. Forse la democratizzazione del design del linguaggio sembra un po 'spaventosa! I creatori di librerie TH devono documentare bene e definire chiaramente il significato e i risultati degli strumenti che forniscono. Un buon esempio di TH di principio è il pacchetto derive: http://hackage.haskell.org/package/derive - utilizza un DSL tale che l'esempio di molte derivazioni / specifica / la derivazione effettiva.
- Ti lega a GHC. In teoria un altro compilatore potrebbe implementarlo, ma in pratica dubito che ciò accadrà mai. (Ciò è in contrasto con varie estensioni di sistema di tipo che, sebbene possano essere implementate solo da GHC al momento, potrei facilmente immaginare di essere adottato da altri compilatori lungo la strada e alla fine standardizzato.)
Questo è un punto abbastanza buono: l'API TH è piuttosto grande e goffa. Ri-implementare sembra che potrebbe essere difficile. Tuttavia, ci sono solo alcuni modi per tagliare il problema della rappresentazione degli AST Haskell. Immagino che copiare gli ADT TH e scrivere un convertitore nella rappresentazione AST interna ti farebbe molto strada. Ciò equivarrebbe allo sforzo (non insignificante) di creare haskell-src-meta. Potrebbe anche essere semplicemente reimplementato stampando piuttosto il TH AST e usando il parser interno del compilatore.
Anche se potrei sbagliarmi, non vedo TH come complicato da un'estensione del compilatore, dal punto di vista dell'implementazione. Questo è in realtà uno dei vantaggi di "mantenerlo semplice" e di non avere lo strato fondamentale come sistema di templating teoricamente attraente e verificabile staticamente.
- L'API non è stabile. Quando nuove funzionalità linguistiche vengono aggiunte a GHC e il pacchetto template-haskell viene aggiornato per supportarle, ciò comporta spesso modifiche incompatibili all'indietro dei tipi di dati TH. Se vuoi che il tuo codice TH sia compatibile con più di una versione di GHC devi fare molta attenzione e possibilmente usarlo
CPP
.
Anche questo è un buon punto, ma in qualche modo drammatico. Sebbene ultimamente siano state aggiunte aggiunte API, non sono state ampiamente indotte da rotture. Inoltre, penso che con la citazione AST superiore di cui ho parlato in precedenza, l'API che deve effettivamente essere utilizzata può essere notevolmente ridotta. Se nessuna costruzione / abbinamento necessita di funzioni distinte e viene invece espresso come letterale, la maggior parte delle API scompare. Inoltre, il codice che scrivi porta più facilmente alle rappresentazioni AST per lingue simili a Haskell.
In sintesi, penso che il TH sia un potente strumento semi-trascurato. Meno odio potrebbe portare a un ecosistema più vivace di biblioteche, incoraggiando l'implementazione di più prototipi di funzionalità linguistiche. È stato osservato che TH è uno strumento sopraffatto, che può permetterti / fare / quasi tutto. Anarchia! Bene, sono della mia opinione che questo potere possa permetterti di superare la maggior parte dei suoi limiti e costruire sistemi capaci di approcci di meta-programmazione abbastanza basati su principi. Vale la pena usare brutti hack per simulare l'implementazione "corretta", poiché in questo modo il design dell'implementazione "corretta" diventerà gradualmente chiaro.
Nella mia versione ideale personale del nirvana, gran parte del linguaggio si sposterebbe effettivamente dal compilatore, in librerie di queste varietà. Il fatto che le funzionalità siano implementate come librerie non influenza pesantemente la loro capacità di astrarre fedelmente.
Qual è la risposta tipica di Haskell al codice della caldaia? Astrazione. Quali sono le nostre astrazioni preferite? Funzioni e caratteri da banco!
Typeclasses ci consente di definire un insieme di metodi, che possono quindi essere utilizzati in tutti i modi di funzioni generiche su quella classe. Tuttavia, a parte questo, l'unico modo in cui le classi aiutano a evitare il boilerplate è quello di offrire "definizioni predefinite". Ora ecco un esempio di una funzionalità senza principi!
I set di rilegatura minimi non sono dichiarabili / verificabili dal compilatore. Ciò potrebbe portare a definizioni involontarie che danno fondo a causa della ricorsione reciproca.
Nonostante la grande convenienza e il potere che ciò produrrebbe, non è possibile specificare i valori predefiniti della superclasse, a causa di istanze orfane http://lukepalmer.wordpress.com/2009/01/25/a-world-without-orphans/ Questi ci permetterebbero di correggere gerarchia numerica con garbo!
La ricerca di funzionalità simili a TH per i valori predefiniti del metodo ha portato a http://www.haskell.org/haskellwiki/GHC.Generics . Sebbene sia roba interessante, la mia unica esperienza di debug del codice con questi generici era pressoché impossibile, a causa delle dimensioni del tipo indotto e di ADT complicate come un AST.https://github.com/mgsloan/th-extra/commit/d7784d95d396eb3abdb409a24360beb03731c88c
In altre parole, questo è andato dopo le funzionalità fornite da TH, ma ha dovuto innalzare un intero dominio della lingua, la lingua di costruzione, in una rappresentazione del sistema di tipo. Anche se riesco a vederlo funzionare bene per il tuo problema comune, per quelli complessi, sembra incline a produrre una pila di simboli molto più terrificante della pirateria informatica.
TH fornisce un calcolo a livello di valore in fase di compilazione del codice di output, mentre generics ti costringe a sollevare la parte di corrispondenza / ricorsione del modello nel codice nel sistema di tipi. Anche se questo limita l'utente in alcuni modi abbastanza utili, non credo che ne valga la pena.
Penso che il rifiuto di TH e metaprogrammi simili a lisp abbiano portato alla preferenza verso cose come i valori predefiniti del metodo piuttosto che più macroespansione più flessibile come le dichiarazioni di istanze. La disciplina di evitare cose che potrebbero portare a risultati imprevisti è saggia, tuttavia, non dovremmo ignorare che il sistema di tipi capace di Haskell consente metaprogrammi più affidabili rispetto a molti altri ambienti (controllando il codice generato).