270 votes

Qu'y a-t-il de si mauvais dans Template Haskell ?

Il semble que Template Haskell soit souvent considéré par la communauté Haskell comme une commodité malheureuse. Il est difficile d'exprimer exactement ce que j'ai observé à cet égard, mais considérez ces quelques exemples

J'ai vu plusieurs articles de blog où les gens font des choses très intéressantes avec Template Haskell, permettant une syntaxe plus jolie qui ne serait tout simplement pas possible en Haskell normal, ainsi qu'une réduction considérable du boilerplate. Alors pourquoi Template Haskell est-il ainsi méprisé ? Qu'est-ce qui le rend indésirable ? Dans quelles circonstances faut-il éviter Template Haskell, et pourquoi ?

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.

176voto

dflemstr Points 18999

L'une des raisons d'éviter Template Haskell est qu'il n'est pas du tout sûr au niveau des types, ce qui va à l'encontre de "l'esprit de Haskell". En voici quelques exemples :

  • Vous n'avez aucun contrôle sur le type d'AST Haskell qu'un morceau de code TH va générer, au-delà de l'endroit où il apparaîtra ; vous pouvez avoir une valeur de type Exp mais vous ne savez pas si c'est une expression qui représente une [Char] ou un (a -> (forall b . b -> c)) ou autre. La TH serait plus fiable si l'on pouvait exprimer qu'une fonction ne peut générer que des expressions d'un certain type, ou que des déclarations de fonctions, ou que des modèles de correspondance de constructeurs de données, etc.
  • Vous pouvez générer des expressions qui ne compilent pas. Vous avez généré une expression qui fait référence à une variable libre foo qui n'existe pas ? Pas de chance, vous ne le verrez que lorsque vous utiliserez votre générateur de code, et seulement dans les circonstances qui déclenchent la génération de ce code particulier. Il est également très difficile de faire des tests unitaires.

TH est aussi carrément dangereux :

  • Le code qui s'exécute au moment de la compilation peut faire n'importe quoi. IO y compris le lancement de missiles ou le vol de votre carte de crédit. Vous ne voulez pas avoir à parcourir chaque paquet cabal que vous téléchargez à la recherche d'exploits TH.
  • TH peut accéder aux fonctions et définitions "privées du module", rompant complètement l'encapsulation dans certains cas.

Il y a ensuite certains problèmes qui rendent les fonctions TH moins agréables à utiliser en tant que développeur de bibliothèque :

  • Le code TH n'est pas toujours composable. Disons que quelqu'un crée un générateur de lentilles, et le plus souvent, ce générateur sera structuré de telle sorte qu'il ne pourra être appelé directement que par l'"utilisateur final", et non par un autre code TH, en prenant par exemple en paramètre une liste de constructeurs de types pour lesquels générer des lentilles. Il est délicat de générer cette liste en code, alors que l'utilisateur n'a qu'à écrire generateLenses [''Foo, ''Bar] .
  • Les développeurs n'ont même pas connaître que le code TH peut être composé. Saviez-vous que vous pouvez écrire forM_ [''Foo, ''Bar] generateLens ? Q est juste une monade, vous pouvez donc utiliser toutes les fonctions habituelles sur elle. Certaines personnes ne le savent pas, et à cause de cela, elles créent de multiples versions surchargées de fonctions essentiellement identiques, avec la même fonctionnalité, et ces fonctions conduisent à un certain effet de gonflement. En outre, la plupart des gens écrivent leurs générateurs dans le format Q même lorsqu'ils n'ont pas à le faire, ce qui revient à écrire bla :: IO Int; bla = return 3 vous donnez à une fonction plus d'"environnement" qu'elle n'en a besoin, et les clients de la fonction sont tenus de fournir cet environnement en conséquence.

Enfin, certains éléments rendent les fonctions TH moins agréables à utiliser en tant qu'utilisateur final :

  • Opacité. Lorsqu'une fonction TH est de type Q Dec il peut générer absolument n'importe quoi au niveau supérieur d'un module, et vous n'avez absolument aucun contrôle sur ce qui sera généré.
  • Monolithisme. Vous ne pouvez pas contrôler ce que génère une fonction TH à moins que le développeur ne l'autorise ; si vous trouvez une fonction qui génère une interface de base de données et une interface de sérialisation JSON, vous ne pouvez pas dire : "Non, je ne veux que l'interface de base de données, merci ; je vais créer ma propre interface JSON".
  • Temps d'exécution. Le code TH prend un temps relativement long pour s'exécuter. Le code est interprété à nouveau à chaque fois qu'un fichier est compilé, et souvent, une tonne de paquets sont requis par le code TH en cours d'exécution, qui doivent être chargés. Cela ralentit considérablement le temps de compilation.

6 votes

Ajoutez à cela le fait que template-haskell a historiquement été très mal documenté. (Bien que je vienne de regarder à nouveau et il semble que les choses soient au moins marginalement meilleures maintenant). De plus, pour comprendre template-haskell, vous devez essentiellement comprendre la grammaire du langage Haskell, ce qui impose une certaine complexité (ce n'est pas Scheme). Ces deux éléments ont contribué à ce que je ne prenne pas la peine de comprendre TH lorsque j'étais un débutant en Haskell.

17 votes

N'oubliez pas que l'utilisation de Template Haskell signifie soudainement que l'ordre des déclarations a de l'importance ! TH n'est tout simplement pas intégré aussi étroitement qu'on pourrait l'espérer étant donné le poli lisse de Haskell (que ce soit 1.4, 98, 2010 ou même Glasgow).

13 votes

Vous pouvez raisonner sur Haskell sans trop de difficulté, il n'y a pas de telle garantie sur Template Haskell.

54voto

glaebhoerl Points 3182

Il s'agit uniquement de mon opinion personnelle.

  • C'est laid à utiliser. $(fooBar ''Asdf) n'a pas l'air bien. Superficiel, bien sûr, mais ça contribue.

  • C'est encore plus moche à écrire. La citation fonctionne parfois, mais la plupart du temps, il faut faire de la greffe AST manuelle et de la plomberie. Le site API est grande et difficile à manier, il y a toujours un grand nombre 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, et ainsi de suite). C'est ennuyeux et répétitif à écrire et suffisamment compliqué pour ne pas être mécanique. Le site proposition de réforme aborde certains de ces points (en rendant les citations plus largement applicables).

  • La restriction de l'étape est un enfer. L'impossibilité d'épisser des fonctions définies dans le même module en est la plus petite partie : l'autre conséquence est que si vous avez un épissage de haut niveau, tout ce qui suit dans le module sera hors de portée de 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.

  • C'est de l'indiscipline. 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. Pour les classes de types, vous pouvez souvent formuler des lois auxquelles les instances doivent obéir et que les clients peuvent assumer. Si vous utilisez la fonction nouvelle fonction générique 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". Template Haskell, d'un autre côté, n'est que des macros. 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.

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

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

  • Il existe un principe général selon lequel il faut utiliser le bon outil pour le travail et le plus petit qui suffira, et dans cette analogie Template Haskell est quelque chose comme ceci . S'il existe un moyen de le faire qui ne soit pas Template Haskell, il est généralement préférable.

L'avantage de Template Haskell est que vous pouvez faire des choses avec lui que vous ne pourriez pas faire autrement, et c'est un gros avantage. La plupart du temps, les choses pour lesquelles TH est utilisé ne pourraient être faites autrement que si elles étaient implémentées directement en tant que fonctionnalités du compilateur. TH est extrêmement utile à la fois parce qu'il vous permet de faire ces choses et parce qu'il vous permet de prototyper des extensions potentielles du compilateur d'une manière beaucoup plus légère et réutilisable (voir les différents paquets de lentilles, par exemple).

Pour résumer la raison pour laquelle je pense qu'il y a des sentiments négatifs envers Template Haskell : Il résout beaucoup de problèmes, mais pour chaque problème qu'il résout, on a l'impression qu'il devrait y avoir une meilleure solution, plus élégante, plus disciplinée, mieux adaptée à la résolution de ce problème, une solution qui ne résout pas le problème en générant automatiquement le boilerplate, mais en supprimant la nécessité de ont la plaque de cuisson.

* Bien que j'ai souvent l'impression que CPP a un meilleur rapport poids/puissance pour les problèmes qu'il peut résoudre.

EDIT 23-04-14 : Ce que j'essayais souvent de dire dans ce qui précède, et que je n'ai fait que récemment, c'est qu'il y a une distinction importante entre l'abstraction et la déduplication. Une abstraction correcte a souvent comme effet secondaire la déduplication, et la duplication est souvent un signe révélateur d'une abstraction inadéquate, mais ce n'est pas pour cela qu'elle est précieuse. Une abstraction correcte est ce qui rend le code correct, compréhensible et maintenable. La déduplication ne fait que le rendre plus court. Template Haskell, comme les macros en général, est un outil de déduplication.

0 votes

"Le CPP a un meilleur rapport poids/puissance pour les problèmes qu'il peut résoudre". En effet. Et dans C99, il peut résoudre tout ce que vous voulez. Considérez ceci : COS , Chaos . Je ne comprends pas non plus pourquoi les gens pensent que la génération d'AST est meilleure. C'est juste plus d'ambiguïté et moins orthogonal aux autres caractéristiques du langage.

0 votes

Le lien de votre proposition de réforme est maintenant mort.

31voto

mgsloan Points 601

J'aimerais aborder quelques-uns des points soulevés par dflemstr.

Je ne trouve pas que le fait que vous ne puissiez pas vérifier la typographie de TH soit si inquiétant. Pourquoi ? Parce que même s'il y a une erreur, ce sera toujours au moment de la compilation. Je ne sais pas si cela renforce mon argument, mais c'est similaire dans l'esprit aux erreurs que vous recevez lorsque vous utilisez des templates en C++. Je pense que ces erreurs sont cependant plus compréhensibles que celles du C++, car vous obtiendrez une version imprimée du code généré.

Si une expression TH / quasi-quoter fait quelque chose qui est si avancé que des coins délicats peuvent être cachés, alors peut-être est-ce mal conseillé ?

J'enfreins souvent cette règle avec les quasi-quoteurs sur lesquels j'ai travaillé récemment (en utilisant haskell-src-exts / meta). https://github.com/mgsloan/quasi-extras/tree/master/examples . Je sais que cela introduit quelques bogues, comme l'impossibilité d'effectuer des épissures dans les compréhensions de listes généralisées. Cependant, je pense qu'il y a une bonne chance pour que certaines des idées de la section http://hackage.haskell.org/trac/ghc/blog/Template%20Haskell%20Proposal se retrouvera dans le compilateur. En attendant, les bibliothèques permettant d'analyser Haskell en arbres TH constituent une approximation presque parfaite.

En ce qui concerne la vitesse de compilation / les dépendances, nous pouvons utiliser le paquet "zeroth" pour mettre en ligne le code généré. C'est au moins agréable pour les utilisateurs d'une bibliothèque donnée, mais nous ne pouvons pas faire beaucoup mieux dans le cas de l'édition de la bibliothèque. Les dépendances TH peuvent-elles gonfler les binaires générés ? Je pensais que cela laissait de côté tout ce qui n'était pas référencé par le code compilé.

La restriction de la mise en scène / le fractionnement des étapes de compilation du module Haskell est nul.

RE Opacité : C'est la même chose pour toute fonction de bibliothèque que vous appelez. Vous n'avez aucun contrôle sur ce que Data.List.groupBy fera. Vous avez juste une "garantie" raisonnable / convention que les numéros de version vous disent quelque chose sur la compatibilité. Il s'agit en quelque sorte d'une question différente de changement quand.

C'est là que l'utilisation de zeroth s'avère utile : vous avez déjà mis en place un système de gestion des versions des fichiers générés, de sorte que vous saurez toujours si la forme du code généré a changé. La consultation des différences peut être un peu difficile, cependant, pour de grandes quantités de code généré, c'est donc un endroit où une meilleure interface de développement serait pratique.

RE Monolithisme : Vous pouvez certainement post-traiter les résultats d'une expression TH, en utilisant votre propre code de compilation. Il n'y aurait pas beaucoup de code pour filtrer sur le type / nom de déclaration de haut niveau. On pourrait même imaginer d'écrire une fonction qui le ferait de manière générique. Pour modifier / démonolithiser les quasi-quoters, vous pouvez effectuer une recherche par motif sur "QuasiQuoter" et extraire les transformations utilisées, ou en créer une nouvelle en fonction de l'ancienne.

1 votes

Concernant Opacité/Monolithisme : Vous pouvez bien sûr passer par un [Dec] et supprimer les éléments que vous ne voulez pas, mais disons que la fonction lit un fichier de définition externe lors de la génération de l'interface JSON. Ce n'est pas parce que vous n'utilisez pas cette Dec ne fait pas cesser le générateur de chercher le fichier de définition, ce qui fait échouer la compilation. Pour cette raison, il serait bien d'avoir une version plus restrictive de la fonction Q qui vous permettrait de générer de nouveaux noms (et d'autres choses du même genre) mais ne permettrait pas de IO pour que, comme vous le dites, on puisse filtrer ses résultats et faire d'autres compositions.

1 votes

Je suis d'accord, il devrait y avoir une version non-IO de Q / citation ! Cela permettrait également de résoudre - stackoverflow.com/questions/7107308/ . Une fois que l'on s'est assuré que le code compilé ne fait rien de dangereux, il semble que l'on puisse simplement exécuter le vérificateur de sécurité sur le code résultant, et s'assurer qu'il ne fait pas référence à des éléments privés.

1 votes

Avez-vous déjà rédigé votre contre-argumentation ? J'attends toujours le lien promis. Pour ceux qui cherchent l'ancien contenu de cette réponse : stackoverflow.com/revisions/10859441/1 et pour ceux qui peuvent voir le contenu supprimé : stackoverflow.com/revisions/10913718/6

15voto

mgsloan Points 601

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

4 votes

Cette réponse ne se suffit pas à elle-même : vous faites un tas de références à une autre réponse que je dois aller chercher avant de pouvoir lire la vôtre correctement.

0 votes

C'est vrai. J'ai essayé de préciser ce dont on parlait malgré tout. Je vais peut-être éditer pour mettre en ligne les points d'illisuis.

0 votes

Je dois dire que "sans principe" est peut-être un mot plus fort que je n'aurais dû utiliser, avec certaines connotations que je n'avais pas l'intention d'utiliser - ce n'est certainement pas sans scrupules ! Ce point est celui avec lequel j'ai eu le plus de mal, parce que j'ai ce sentiment ou cette idée non formée dans ma tête et que j'ai du mal à la mettre en mots, et "sans principes" était l'un des mots que j'ai saisis qui se situait quelque part à proximité. "Discipliné" est probablement plus proche. N'ayant pas de formulation claire et succincte, j'ai cherché des exemples à la place. Si quelqu'un peut m'expliquer plus clairement ce que j'ai probablement voulu dire, je l'apprécierais !

8voto

Joachim Breitner Points 9238

Un problème plutôt pragmatique avec Template Haskell est qu'il ne fonctionne que lorsque l'interpréteur de bytecode de GHC est disponible, ce qui n'est pas le cas sur toutes les architectures. Ainsi, si votre programme utilise Template Haskell ou repose sur des bibliothèques qui l'utilisent, il ne fonctionnera pas sur les machines équipées d'un processeur ARM, MIPS, S390 ou PowerPC.

Ceci est pertinent dans la pratique : git-annex git-annex est un outil écrit en Haskell qu'il est logique d'exécuter sur des machines qui se soucient du stockage, ces machines ayant souvent des processeurs autres que I386. Personnellement, j'exécute git-annex sur une machine de type NSLU 2 (32 Mo de RAM, CPU 266MHz ; saviez-vous que Haskell fonctionne bien sur ce type de matériel ?) Si elle devait utiliser Template Haskell, ce ne serait pas possible.

(La situation de GHC sur ARM s'est beaucoup améliorée ces derniers temps et je pense que la version 7.4.2 fonctionne même, mais la question reste posée).

1 votes

"Template Haskell s'appuie sur le compilateur et l'interpréteur bytecode intégré de GHC pour exécuter les expressions de découpage". - haskell.org/ghc/docs/7.6.2/html/guide_utilisateurs/

3 votes

Ah, j'ai compris -- non, TH ne fonctionnera pas sans l'interpréteur bytecode, mais c'est distinct de (bien que pertinent pour) ghci. Je ne serais pas surpris s'il y avait une relation parfaite entre la disponibilité de ghci et la disponibilité de l'interpréteur bytecode, étant donné que ghci dépend de l'interpréteur bytecode, mais le problème ici est le manque de l'interpréteur bytecode, pas le manque de ghci spécifiquement.

1 votes

(d'ailleurs, ghci a un support étonnamment pauvre pour l'utilisation interactive de TH. essayer ghci -XTemplateHaskell <<< '$(do Language.Haskell.TH.runIO $ (System.Random.randomIO :: IO Int) >>= print; [| 1 |] )' )

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X