Cette réponse est une réponse aux questions soulevées par illissius, point par point :
- C'est laid à utiliser. $(fooBar ''Asdf) n'est tout simplement pas joli. C'est superficiel, certes, mais cela contribue.
Je suis d'accord. J'ai l'impression que $( ) a été choisi pour donner l'impression de faire partie du langage - en utilisant la palette de symboles familiers de Haskell. Cependant, c'est exactement ce que vous ne voulez pas dans les symboles utilisés pour l'épissage de vos macros. Ils se fondent définitivement trop dans le paysage, et cet aspect cosmétique est très important. J'aime l'aspect des {{ }} pour les épissures, car ils sont visuellement distincts.
- C'est encore plus moche à écrire. La citation fonctionne parfois, mais la plupart du temps, vous devez effectuer des greffes AST manuelles et de la plomberie. L'[API][1] est grande et difficile à manier, il y a toujours beaucoup de cas dont vous ne vous souciez pas mais que vous devez quand même distribuer, et les cas dont vous vous souciez ont tendance à être présents sous de multiples formes similaires mais non identiques (data vs newtype, record-style vs normal constructors, etc.) C'est ennuyeux et répétitif à écrire et suffisamment compliqué pour ne pas être mécanique. La [proposition de réforme][2] aborde certains de ces points (en rendant les citations plus largement applicables).
Je suis également d'accord avec cela, cependant, comme le font remarquer certains des commentaires dans "New Directions for TH", le manque de bonnes citations AST prêtes à l'emploi n'est pas un défaut critique. Dans ce paquet WIP, je cherche à résoudre ces problèmes sous forme de bibliothèque : https://github.com/mgsloan/quasi-extras . Jusqu'à présent, j'autorise l'épissage à un peu plus d'endroits que d'habitude et je peux effectuer une correspondance de motifs sur les AST.
- La restriction de l'étape est un enfer. Le fait de ne pas pouvoir splice les fonctions définies dans le même module en est la plus petite partie : l'autre conséquence est que si vous avez un splice de haut niveau, tout ce qui le suit dans le module sera hors de portée de tout ce qui le précède. D'autres langages ayant cette propriété (C, C++) rendent cela possible en vous permettant de déclarer des choses en avance, mais Haskell ne le fait pas. Si vous avez besoin de références cycliques entre les déclarations épissées ou leurs dépendances et dépendances, vous êtes généralement fichu.
J'ai déjà rencontré le problème des définitions de TH cycliques qui sont impossibles... C'est assez ennuyeux. Il y a une solution, mais c'est moche - envelopper les choses impliquées dans la dépendance cyclique dans une expression TH qui combine toutes les déclarations générées. L'un de ces générateurs de déclarations pourrait simplement être un quasi-quotient qui accepte le code Haskell.
- C'est sans principe. Ce que je veux dire par là, c'est que la plupart du temps, lorsque vous exprimez une abstraction, il y a une sorte de principe ou de concept derrière cette abstraction. Pour beaucoup d'abstractions, le principe derrière elles peut être exprimé dans leurs types. Lorsque vous définissez une classe de type, vous pouvez souvent formuler des lois auxquelles les instances doivent obéir et que les clients peuvent assumer. Si vous utilisez la [nouvelle fonctionnalité générique][3] de GHC pour abstraire la forme d'une déclaration d'instance sur n'importe quel type de données (dans les limites), vous pouvez dire "pour les types somme, cela fonctionne comme ceci, pour les types produit, cela fonctionne comme cela". Mais Template Haskell n'est que des macros idiotes. Ce n'est pas de l'abstraction au niveau des idées, mais de l'abstraction au niveau des AST, ce qui est mieux, mais seulement modestement, que l'abstraction au niveau du texte brut.
C'est seulement sans principe si vous faites des choses sans principe avec. La seule différence est qu'avec les mécanismes d'abstraction implémentés par le compilateur, vous avez plus de confiance dans le fait que l'abstraction n'est pas fuyante. La démocratisation de la conception des langages est peut-être un peu effrayante ! Les créateurs de bibliothèques TH doivent bien documenter et définir clairement la signification et les résultats des outils qu'ils fournissent. Le paquet derive est un bon exemple de TH fondé sur des principes : http://hackage.haskell.org/package/derive - il utilise un DSL tel que l'exemple de nombreuses dérivations /spécifie/ la dérivation réelle.
- Il vous lie à GHC. En théorie, un autre compilateur pourrait l'implémenter, mais en pratique, je doute que cela arrive un jour. (Ceci est en contraste avec diverses extensions du système de type qui, bien qu'elles puissent être seulement implémentées par GHC pour le moment, je pourrais facilement imaginer être adoptées par d'autres compilateurs sur la route et éventuellement standardisées).
C'est un bon point - l'API de TH est assez grande et encombrante. La réimplémenter pourrait s'avérer difficile. Cependant, il n'y a que quelques façons de trancher le problème de la représentation des AST Haskell. J'imagine que copier les ADT de TH et écrire un convertisseur vers la représentation interne de l'AST vous permettrait de faire une bonne partie du chemin. Cela serait équivalent à l'effort (non négligeable) de création de haskell-src-meta. Il pourrait également être simplement réimplémenté en imprimant l'AST TH et en utilisant l'analyseur interne du compilateur.
Bien que je puisse me tromper, je ne pense pas que TH soit une extension de compilateur si compliquée, du point de vue de l'implémentation. C'est en fait l'un des avantages de "garder les choses simples" et de ne pas avoir la couche fondamentale comme un système de templating théoriquement attrayant et statiquement vérifiable.
- L'API n'est pas stable. Lorsque de nouvelles fonctionnalités du langage sont ajoutées à GHC et que le paquetage template-haskell est mis à jour pour les supporter, cela implique souvent des modifications rétrocompatibles des types de données TH. Si vous voulez que votre code TH soit compatible avec plus d'une version de GHC, vous devez être très prudent et éventuellement utiliser la méthode suivante
CPP
.
C'est également un bon point, mais quelque peu dramatisé. Bien qu'il y ait eu des ajouts d'API récemment, ils n'ont pas été très destructeurs. De plus, je pense qu'avec la citation AST supérieure que j'ai mentionnée plus tôt, l'API qui doit réellement être utilisée peut être réduite de manière très substantielle. Si aucune construction / correspondance n'a besoin de fonctions distinctes, et sont plutôt exprimées comme des littéraux, alors la plupart de l'API disparaît. De plus, le code que vous écrivez se porterait plus facilement vers des représentations AST pour des langages similaires à Haskell.
En résumé, je pense que le TH est un outil puissant, semi-négligé. Moins de haine pourrait conduire à un écosystème de bibliothèques plus vivant, encourageant l'implémentation de plus de prototypes de fonctionnalités du langage. Il a été observé que TH est un outil surpuissant, qui peut vous permettre de /faire/ presque tout. L'anarchie ! Eh bien, je pense que cette puissance peut vous permettre de surmonter la plupart de ses limites, et de construire des systèmes capables d'approches de méta-programmation tout à fait fondées sur des principes. Cela vaut la peine d'utiliser de vilaines bidouilles pour simuler l'implémentation "correcte", car de cette façon la conception de l'implémentation "correcte" deviendra progressivement claire.
Dans ma version idéale personnelle du nirvana, une grande partie du langage sortirait du compilateur pour être intégrée dans des bibliothèques de ce type. Le fait que les fonctionnalités soient implémentées en tant que bibliothèques n'influence pas fortement leur capacité à abstraire fidèlement.
Quelle est la réponse typique de Haskell au code passe-partout ? L'abstraction. Quelles sont nos abstractions préférées ? Les fonctions et les classes de type !
Les classes de type nous permettent de définir un ensemble de méthodes, qui peuvent ensuite être utilisées dans toutes sortes de fonctions génériques sur cette classe. Cependant, à part cela, la seule façon dont les classes permettent d'éviter le boilerplate est de proposer des "définitions par défaut". Voici un exemple de fonctionnalité sans principe !
-
Les ensembles de liaisons minimales ne sont pas déclarables / vérifiables par le compilateur. Cela pourrait conduire à des définitions par inadvertance qui produisent un fond en raison de la récursion mutuelle.
-
Malgré la grande commodité et la puissance que cela apporterait, vous ne pouvez pas spécifier les valeurs par défaut des superclasses, en raison des instances orphelines. http://lukepalmer.wordpress.com/2009/01/25/a-world-without-orphans/ Cela nous permettrait de corriger la hiérarchie numérique de manière élégante !
-
Le fait de rechercher des capacités de type TH pour les défauts de méthode a conduit à http://www.haskell.org/haskellwiki/GHC.Generics . Bien que ce soit cool, ma seule expérience de débogage de code utilisant ces génériques a été quasi-impossible, en raison de la taille du type induit pour un ADT aussi compliqué qu'un AST. https://github.com/mgsloan/th-extra/commit/d7784d95d396eb3abdb409a24360beb03731c88c
En d'autres termes, il s'agissait de reprendre les fonctionnalités fournies par le TH, mais il fallait que tout un domaine du langage, le langage de construction, soit représenté par un système de types. Bien que je puisse imaginer que cela fonctionne bien pour votre problème commun, pour les problèmes complexes, cela semble susceptible de produire une pile de symboles bien plus terrifiante que le piratage du TH.
Le TH vous permet de calculer le code de sortie au niveau de la valeur au moment de la compilation, tandis que les génériques vous obligent à intégrer la partie du code relative au filtrage et à la récursion dans le système de types. Bien que cela limite l'utilisateur de quelques façons assez utiles, je ne pense pas que la complexité en vaille la peine.
Je pense que le rejet de la TH et de la métaprogrammation de type lisp a conduit à la préférence pour des choses comme les défauts de méthode au lieu de déclarations d'instances plus flexibles, de type macro-expansion. La discipline consistant à éviter les choses qui pourraient conduire à des résultats imprévus est sage, cependant, nous ne devrions pas ignorer que le système de types performant de Haskell permet une métaprogrammation plus fiable que dans de nombreux autres environnements (en vérifiant le code généré).
0 votes
Voté pour fermer : appartient à programmers.stackexchange.com - Q&A pour les programmeurs professionnels intéressés par les questions conceptuelles sur le développement de logiciels.
60 votes
Je ne suis pas d'accord avec le vote pour le déplacement ; je pose cette question dans le même esprit que j'ai posé Qu'est-ce qu'il y a de si mal avec Lazy I/O ? et je m'attends à voir des réponses de la même façon. Je suis prêt à reformuler la question si cela peut aider.
55 votes
@ErikPhilips Pourquoi ne laissez-vous pas les personnes qui fréquentent ce tag décider de sa place ici ? On dirait que votre seule interaction avec la communauté Haskell consiste à rabaisser ses questions.
5 votes
@GabrielGonzalez il est évident avec la question et la réponse actuelle qu'elle ne suit pas la politique de l'UE. FAQ sous quel type de question je ne devrais pas poser ici : il n'y a pas de problème réel à résoudre . La question n'a pas de problème spécifique au code résolu et est seulement de nature conceptuelle. Et d'après la question Dois-je aussi éviter les modèles haskell ? il tombe dans le Stack Overflow n'est pas un moteur de recommandation .
31 votes
@ErikPhilips L'aspect du moteur de recommandation n'est pas pertinent pour cette question, car vous remarquerez qu'il s'agit d'une décision entre différents outils (par exemple, "dites-moi quel langage je devrais utiliser"). En revanche, je demande simplement une explication concernant Template Haskell en particulier, et la FAQ indique que "si votre motivation est "j'aimerais que d'autres m'expliquent [blanc]", alors vous êtes probablement OK". Comparez avec, par exemple, GOTO est toujours considéré comme nuisible ?
5 votes
J'ai modifié le titre et les phrases de conclusion pour mettre l'accent moins sur le comportement de la communauté et plus sur Template Haskell lui-même. Bien que j'aie déjà accepté une réponse, je serais plus qu'heureux de voir plus de réponses s'il y a plus de terrain à couvrir.
32 votes
Je vote pour la réouverture. Ce n'est pas parce que c'est une question de niveau supérieur qu'elle n'est pas bonne.
1 votes
Template Haskell transforme Haskell en un langage multi-paradigme (fonctionnel paresseux + méta-programmation). Je suis content que TH existe - il est utile et certaines personnes font des choses vraiment intelligentes avec lui (par exemple Flask de Geoffrey Mainland) mais personnellement j'aime coder juste dans le paradigme fonctionnel.