Cosa c'è di così brutto in Template Haskell?


252

Sembra che Template Haskell sia spesso visto dalla comunità Haskell come una sfortunata comodità. È difficile esprimere a parole esattamente ciò che ho osservato al riguardo, ma considera questi pochi esempi

Ho visto vari post sul blog in cui le persone fanno cose piuttosto ordinate con Template Haskell, abilitando una sintassi più bella che semplicemente non sarebbe possibile nella normale Haskell, oltre a un'enorme riduzione del plateplate. Allora perché Template Haskell è guardato in basso in questo modo? Cosa lo rende indesiderabile? In quali circostanze dovrebbe essere evitato Template Haskell e perché?


56
Non sono d'accordo con il voto a passare; Sto ponendo questa domanda con lo stesso spirito che ho posto. Cosa c'è di così brutto in Lazy I / O? e mi aspetto di vedere le stesse risposte. Sono aperto a riformulare la domanda se ciò potrebbe aiutare.
Dan Burton,

51
@ErikPhilips Perché non lasci che le persone che frequentano questo tag decidano se appartiene qui? Sembra che la tua unica interazione con la comunità di Haskell sia quella di porre le sue domande.
Gabriel Gonzalez,

5
@GabrielGonzalez è ovvio con la domanda attuale e rispondi che non segue le FAQ sotto quale tipo di domanda non dovrei porre qui: non c'è nessun problema reale da risolvere . La domanda non ha risolto alcun problema specifico di codice ed è solo di natura concettuale. E in base alla domanda dovrei evitare l'hashell del modello, anche se cade nello Stack Overflow non è un motore di raccomandazione .
Erik Philips,

27
@ErikPhilips L'aspetto del motore di raccomandazione è irrilevante per questa domanda, perché noterai che si riferisce a una decisione tra diversi strumenti (ad esempio "dimmi quale lingua dovrei usare"). Al contrario, sto semplicemente chiedendo una spiegazione specifica di Template Haskell, e la FAQ afferma "se la tua motivazione è" Vorrei che gli altri mi spiegassero [vuoto] ", allora probabilmente stai bene. Confronta con, ad esempio, GOTO ancora considerato dannoso?
Dan Burton,

29
Votazione per riaprire. Solo perché è una domanda di livello superiore, non significa che non sia una buona domanda.
György Andrasek il

Risposte:


171

Uno dei motivi per evitare Template Haskell è che nel suo insieme non è affatto sicuro per i tipi, andando così contro gran parte dello "spirito di Haskell". Ecco alcuni esempi di questo:

  • Non hai alcun controllo su quale tipo di Haskell AST genererà un pezzo di codice TH, oltre a dove apparirà; puoi avere un valore di tipo Exp, ma non sai se si tratta di un'espressione che rappresenta a [Char]o a (a -> (forall b . b -> c))o qualunque altra cosa. TH sarebbe più affidabile se si potesse esprimere che una funzione può generare solo espressioni di un certo tipo, o solo dichiarazioni di funzioni, o solo modelli di corrispondenza dei costruttori di dati, ecc.
  • È possibile generare espressioni che non vengono compilate. Hai generato un'espressione che fa riferimento a una variabile libera fooche non esiste? Per fortuna, lo vedrai solo quando utilizzi effettivamente il tuo generatore di codice e solo nelle circostanze che innescano la generazione di quel particolare codice. Anche il test unitario è molto difficile.

TH è anche assolutamente pericoloso:

  • Il codice che viene eseguito in fase di compilazione può essere arbitrario IO, incluso il lancio di missili o il furto della carta di credito. Non devi cercare tutti i pacchetti cabala che scarichi mai alla ricerca di exploit TH.
  • TH può accedere a funzioni e definizioni "modulo-private", interrompendo completamente l'incapsulamento in alcuni casi.

Quindi ci sono alcuni problemi che rendono le funzioni TH meno divertenti da usare come sviluppatore di librerie:

  • Il codice TH non è sempre componibile. Diciamo che qualcuno crea un generatore per obiettivi, e il più delle volte, quel generatore sarà strutturato in modo tale da poter essere chiamato solo direttamente dall '"utente finale" e non da altri codici TH, ad esempio prendendo un elenco di costruttori di tipi per generare obiettivi come parametro. È difficile generare tale elenco nel codice, mentre l'utente deve solo scrivere generateLenses [''Foo, ''Bar].
  • Gli sviluppatori non sanno nemmeno che il codice TH può essere composto. Lo sapevi che puoi scrivere forM_ [''Foo, ''Bar] generateLens? Qè solo una monade, quindi puoi usare tutte le solite funzioni su di essa. Alcune persone non lo sanno e, a causa di ciò, creano più versioni sovraccariche essenzialmente delle stesse funzioni con la stessa funzionalità e queste funzioni portano a un certo effetto gonfio. Inoltre, la maggior parte delle persone scrive i propri generatori nella Qmonade anche quando non è necessario, il che è come scrivere bla :: IO Int; bla = return 3; stai dando una funzione più "ambiente" del necessario e i clienti della funzione sono tenuti a fornire quell'ambiente come effetto di quello.

Infine, ci sono alcune cose che rendono le funzioni TH meno divertenti da usare come utente finale:

  • Opacità. Quando una funzione TH ha tipo Q Dec, può generare assolutamente qualsiasi cosa al livello più alto di un modulo e non hai assolutamente alcun controllo su ciò che verrà generato.
  • Monolitismo. Non puoi controllare quanto una funzione TH genera se lo sviluppatore non lo consente; se trovi una funzione che genera un'interfaccia di database e un'interfaccia di serializzazione JSON, non puoi dire "No, voglio solo l'interfaccia del database, grazie; rotolerò la mia interfaccia JSON"
  • Tempo di esecuzione. L'esecuzione del codice TH richiede un tempo relativamente lungo. Il codice viene interpretato di nuovo ogni volta che viene compilato un file e spesso sono richiesti un sacco di pacchetti dal codice TH in esecuzione, che devono essere caricati. Questo rallenta notevolmente il tempo di compilazione.

4
Aggiungete a ciò il fatto che template-haskell è stato storicamente molto poco documentato. (Anche se ho appena guardato di nuovo e sembra che le cose siano almeno marginalmente migliori ora.) Inoltre, per capire template-haskell, devi essenzialmente capire la grammatica del linguaggio Haskell, che impone una certa complessità (non è uno schema). Queste due cose mi hanno contribuito intenzionalmente a non preoccuparmi di capire TH quando ero un principiante di Haskell.
mightybyte,

14
Non dimenticare che l'uso di Template Haskell significa che l'ordine delle dichiarazioni è importante! Il TH non è integrato così strettamente come si potrebbe sperare dato lo smalto liscio di Haskell (sia 1.4, '98, 2010 o persino Glasgow).
Thomas M. DuBuisson,

13
Puoi ragionare su Haskell senza troppe difficoltà, non esiste una tale garanzia su Template Haskell.
agosto

15
E cosa è successo alla promessa di Oleg di un'alternativa sicura al TH? Mi riferisco al suo lavoro basato sul suo documento "Finalmente senza tag, parzialmente valutato" e altri suoi appunti qui . Sembrava così promettente quando l'hanno annunciato e poi non ho mai sentito un'altra parola al riguardo.
Gabriel Gonzalez,


53

Questa è solo la mia opinione.

  • È brutto da usare. $(fooBar ''Asdf)semplicemente non sembra carino. Superficiale, certo, ma contribuisce.

  • 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 è grande e ingombrante, ci sono sempre molti casi che non ti interessano ma che devono ancora essere spediti, e i casi che ti interessano tendono ad essere presenti in più forme simili ma non identiche (dati vs. newtype, record stile vs. costruttori normali e così via). Scrivere è noioso e ripetitivo e abbastanza complicato da non essere meccanico. La proposta di riforma affronta in parte questo (rendendo le citazioni più ampiamente applicabili).

  • 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.

  • È indisciplinato. 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. Per le classi di tipi, è spesso possibile formulare leggi a cui le istanze devono obbedire e che i clienti possono assumere. Se si utilizza la nuova funzione generica di GHC per astrarre la forma di una dichiarazione di istanza su qualsiasi tipo di dati (entro i limiti), si può dire "per i tipi di somma, funziona così, per i tipi di prodotto, funziona così". Il modello Haskell, d'altra parte, è solo macro. Non è l'astrazione a livello di idee, ma l'astrazione a livello di AST, che è migliore, ma solo modestamente, dell'astrazione a livello di testo normale. *

  • 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.)

  • 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 usarloCPP .

  • C'è un principio generale secondo cui dovresti usare lo strumento giusto per il lavoro e quello più piccolo che basterà, e in quella analogia Template Haskell è qualcosa del genere . Se c'è un modo per farlo che non è Template Haskell, è generalmente preferibile.

Il vantaggio di Template Haskell è che puoi farci cose che non potresti fare diversamente, ed è grande. La maggior parte delle volte le cose per cui TH viene utilizzato potrebbero altrimenti essere eseguite solo se fossero implementate direttamente come funzionalità del compilatore. TH è estremamente utile avere entrambi perché ti consente di fare queste cose e perché ti consente di prototipare le potenziali estensioni del compilatore in un modo molto più leggero e riutilizzabile (vedi i vari pacchetti di obiettivi, ad esempio).

Per riassumere perché penso che ci siano sentimenti negativi nei confronti di Template Haskell: risolve molti problemi, ma per ogni dato problema che risolve, sembra che ci dovrebbe essere una soluzione migliore, più elegante e disciplinata più adatta a risolverlo, uno che non risolve il problema generando automaticamente la piastra della caldaia, ma rimuovendo la necessità di avere la piastra della caldaia.

* Anche se sento spesso che CPPha un miglior rapporto peso / potenza per quei problemi che può risolvere.

EDIT 23-04-14: Quello che stavo spesso cercando di ottenere in quanto sopra, e solo recentemente ho capito esattamente, è che c'è una distinzione importante tra astrazione e deduplica. L'astrazione corretta spesso porta alla deduplicazione come effetto collaterale e la duplicazione è spesso un segno rivelatore di astrazione inadeguata, ma non è per questo che ha valore. L'astrazione corretta è ciò che rende il codice corretto, comprensibile e mantenibile. La deduplicazione lo rende solo più breve. Il modello Haskell, come le macro in generale, è uno strumento per la deduplicazione.


"Il CPP ha un migliore rapporto peso / potenza per quei problemi che può risolvere". Infatti. E in C99 può risolvere quello che vuoi. Considera questi: COS , Caos . Inoltre non capisco perché la gente pensi che la generazione di AST sia migliore. È solo più ambiguità e meno ortogonale rispetto ad altre funzioni linguistiche
Britton Kerin,

31

Vorrei affrontare alcuni dei punti sollevati da dflemstr.

Non trovo che il fatto che non si riesca a digitare TH sia così preoccupante. Perché? Perché anche se si verifica un errore, sarà comunque il tempo di compilazione. Non sono sicuro che questo rafforzi la mia tesi, ma questo è simile nello spirito agli errori che ricevi quando usi i template in C ++. Penso che questi errori siano più comprensibili degli errori di C ++, poiché otterrai una versione piuttosto stampata del codice generato.

Se un'espressione TH / quasi-quoter fa qualcosa di così avanzato da nascondere angoli complicati, forse è sconsigliato?

Ho infranto un po 'questa regola con quasi-quoter su cui ho lavorato di recente (usando haskell-src-exts / meta) - https://github.com/mgsloan/quasi-extras/tree/master/examples . So che questo introduce alcuni bug come non riuscire a comprendere le comprensioni generalizzate dell'elenco. Tuttavia, penso che ci siano buone probabilità che alcune idee in http://hackage.haskell.org/trac/ghc/blog/Template%20Haskell%20Proposal finiscano nel compilatore. Fino ad allora, le librerie per l'analisi di Haskell su alberi TH sono un'approssimazione quasi perfetta.

Per quanto riguarda la velocità / dipendenze della compilazione, possiamo usare il pacchetto "zeroth" per incorporare il codice generato. Questo è almeno utile per gli utenti di una determinata libreria, ma non possiamo fare molto meglio nel caso di modificare la libreria. Le dipendenze TH possono gonfiare i binari generati? Ho pensato che fosse escluso tutto ciò a cui non faceva riferimento il codice compilato.

La restrizione di messa in scena / divisione delle fasi di compilazione del modulo Haskell fa schifo.

Opacità RE: è la stessa per qualsiasi funzione di libreria chiamata. Non hai alcun controllo su ciò che farà Data.List.groupBy. Hai solo una ragionevole "garanzia" / convenzione che i numeri di versione ti diano qualcosa sulla compatibilità. Quando è un po 'diverso il cambiamento.

Qui è dove l'uso di zeroth ripaga - stai già eseguendo il controllo delle versioni dei file generati - così saprai sempre quando la forma del codice generato è cambiata. Guardare le differenze potrebbe essere un po 'nodoso, tuttavia, per grandi quantità di codice generato, quindi è un posto in cui sarebbe utile una migliore interfaccia per sviluppatori.

Monolitismo RE: puoi certamente post-elaborare i risultati di un'espressione TH, usando il tuo codice di compilazione. Non sarebbe molto il codice da filtrare in base al tipo / nome della dichiarazione di livello superiore. Cavolo, potresti immaginare di scrivere una funzione che lo fa genericamente. Per la modifica / de-monolitizzazione dei quasiquotatori, è possibile modellare la corrispondenza su "QuasiQuoter" ed estrarre le trasformazioni utilizzate o crearne una nuova in termini di vecchia.


1
Per quanto riguarda l' opacità / il monolitismo: puoi ovviamente camminare attraverso [Dec]e rimuovere elementi che non vuoi, ma diciamo che la funzione legge un file di definizione esterno quando si genera l'interfaccia JSON. Solo perché non lo usi non Decimpedisce al generatore di cercare il file di definizione, facendo fallire la compilazione. Per questo motivo, sarebbe bello avere una versione più restrittiva della Qmonade che ti permetterebbe di generare nuovi nomi (e cose del genere) ma non consentire IO, in modo che, come dici tu, potresti filtrare i suoi risultati / fare altre composizioni .
dflemstr,

1
Sono d'accordo, dovrebbe esserci una versione non-IO di Q / quotation! Questo aiuterebbe anche a risolvere - stackoverflow.com/questions/7107308/… . Una volta verificato che il codice in fase di compilazione non fa nulla di pericoloso, sembra che potresti semplicemente eseguire il controllo di sicurezza sul codice risultante e assicurarti che non faccia riferimento a cose private.
mgsloan

1
Hai mai scritto il tuo controargumento? Sto ancora aspettando il tuo link promesso. Per coloro che cercano i vecchi contenuti di questa risposta: stackoverflow.com/revisions/10859441/1 e per quelli che possono visualizzare i contenuti eliminati: stackoverflow.com/revisions/10913718/6
Dan Burton,

1
esiste una versione più aggiornata di zeroth rispetto a quella sull'hackage? Quello (e il repository darcs) sono stati aggiornati l'ultima volta nel 2009. Entrambe le copie non vengono compilate con un ghc corrente (7.6).
aavogt,

1
@aavogt tgeeky stava lavorando per risolverlo, ma non credo che abbia finito: github.com/technogeeky/zeroth Se nessuno lo fa / crea qualcosa per questo, alla fine ci proverò.
mgsloan

16

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 usarloCPP .

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).


4
Questa risposta non sta molto bene da sola: stai facendo un sacco di riferimenti a un'altra risposta che devo andare a trovare prima di poter leggere correttamente la tua.
Ben Millwood,

È vero. Ho cercato di chiarire di cosa si parlava nonostante. Forse modificherò per incorporare i punti di Illisuis.
mgsloan,

Dovrei dire che forse "senza principi" è una parola più forte di quanto avrei dovuto usare, con alcune connotazioni che non intendevo - non è certo senza scrupoli! Quel punto è stato quello con cui ho avuto più problemi, perché ho questa sensazione o un'idea non formata nella mia testa e ho difficoltà a metterla in parole, e "senza principi" è stata una delle parole che ho capito che era da qualche parte nelle vicinanze. "Disciplinato" è probabilmente più vicino. Non avendo una formulazione chiara e succinta, sono andato invece per alcuni esempi. Se qualcuno potesse spiegarmi più chiaramente cosa volevo dire, lo apprezzerei!
glaebhoerl,

(Penso anche che potresti aver modificato la limitazione della messa in scena e le citazioni da scrivere brutte.)
glaebhoerl

1
Doh! Grazie per la segnalazione! Fisso. Sì, indisciplinato sarebbe probabilmente un modo migliore per dirlo. Penso che la distinzione qui sia in realtà tra meccanismi di astrazione "integrati" e quindi "ben compresi", rispetto a "ad-hoc" o meccanismi di astrazione definiti dall'utente. Suppongo che ciò che intendo sia che si possa concepire una libreria TH che abbia implementato qualcosa di molto simile all'invio di typeclass (anche se in fase di compilazione non in fase di esecuzione)
mgsloan,

8

Un problema piuttosto pragmatico con Template Haskell è che funziona solo quando è disponibile l'interprete bytecode GHC, il che non è il caso di tutte le architetture. Pertanto, se il programma utilizza Template Haskell o si basa su librerie che lo utilizzano, non funzionerà su macchine con una CPU ARM, MIPS, S390 o PowerPC.

Ciò è rilevante nella pratica: git-annex è uno strumento scritto in Haskell che ha senso funzionare su macchine che si preoccupano della memoria, che spesso hanno CPU non i386. Personalmente, eseguo git-annex su un NSLU 2 (32 MB di RAM, CPU a 266 MHz; sapevi che Haskell funziona bene su tale hardware?) Se usasse Template Haskell, questo non è possibile.

(La situazione di GHC su ARM sta migliorando molto in questi giorni e penso che 7.4.2 funzioni anche, ma il punto è ancora valido).


1
"Template Haskell si affida al compilatore e all'interprete bytecode di GHC per eseguire le espressioni di giunzione." - haskell.org/ghc/docs/7.6.2/html/users_guide/…
Joachim Breitner,

2
ah, io questo - no, TH non funzionerà senza l'interprete bytecode, ma questo è distinto da (sebbene rilevante per) ghci. Non sarei sorpreso se ci fosse una relazione perfetta tra disponibilità di ghci e disponibilità dell'interprete bytecode, dato che ghci dipende dall'interprete bytecode, ma il problema è la mancanza dell'interprete bytecode, non la mancanza di ghci nello specifico.
muhmuhten,

1
(per inciso, ghci ha un supporto incredibilmente scarso per l'uso interattivo di TH. try ghci -XTemplateHaskell <<< '$(do Language.Haskell.TH.runIO $ (System.Random.randomIO :: IO Int) >>= print; [| 1 |] )')
muhmuhten

Ok, con ghci mi riferivo alla capacità di GHC di interpretare (anziché compilare) il codice, indipendentemente dal fatto che il codice provenga in modo interattivo nel binario ghci o dalla giunzione TH.
Joachim Breitner,

6

Perché TH è male? Per me, si riduce a questo:

Se hai bisogno di produrre così tanto codice ripetitivo che ti ritrovi a provare a usare TH per generarlo automaticamente, stai sbagliando!

Pensaci. La metà del fascino di Haskell è che il suo design di alto livello ti consente di evitare enormi quantità di codice inutile sulla piastra della caldaia che devi scrivere in altre lingue. Se hai bisogno di generazione di codice in fase di compilazione, in pratica stai dicendo che la tua lingua o il design della tua applicazione ti hanno fallito. E a noi programmatori non piace fallire.

A volte, ovviamente, è necessario. Ma a volte puoi evitare di aver bisogno di TH semplicemente essendo un po 'più intelligente con i tuoi progetti.

(L'altra cosa è che TH è di livello piuttosto basso. Non esiste un progetto di alto livello; sono esposti molti dettagli dell'implementazione interna di GHC. E questo rende l'API soggetta a cambiamenti ...)


Non credo significhi che la tua lingua o la tua applicazione ti hanno deluso, specialmente se consideriamo QuasiQuotes. Ci sono sempre dei compromessi quando si tratta di sintassi. Alcune sintassi descrivono meglio un determinato dominio, quindi a volte potresti voler passare a un'altra sintassi. QuasiQuotes ti consente di alternare elegantemente tra le sintassi. Questo è molto potente ed è utilizzato da Yesod e altre app. Essere in grado di scrivere codice di generazione HTML utilizzando la sintassi che sembra HTML è una funzionalità straordinaria.
CoolCodeBro

@CoolCodeBro Sì, quasi-quoting è abbastanza bello, e suppongo che sia leggermente ortogonale a TH. (Ovviamente è implementato in cima a TH.) Stavo pensando di più sull'utilizzo di TH per generare istanze di classe, o costruire funzioni di più arità, o qualcosa del genere.
MathematicalOrchid,
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.