Этот ответ является ответом на вопросы, поднятые Иллисусом, пункт за пунктом:
- Это некрасиво использовать. $ (fooBar '' Asdf) просто не выглядит красиво. Поверхностно, конечно, но это способствует.
Я согласен. Я чувствую, что $ () был выбран, чтобы выглядеть так, как будто он был частью языка - используя знакомую символьную палитру Хаскелла. Тем не менее, это именно то, что вы / не хотите / хотите в символах, используемых для сплайсинга макросов. Они определенно смешиваются слишком много, и этот косметический аспект очень важен. Мне нравится внешний вид {{}} для сростков, потому что они довольно визуально различимы.
- Еще страшнее писать. Цитирование иногда срабатывает, но большую часть времени вам приходится делать прививку AST вручную и сантехнику. [API] [1] является большим и громоздким, всегда есть много случаев, о которых вы не заботитесь, но по-прежнему нуждаетесь в отправке, и случаи, о которых вы заботитесь, обычно присутствуют в нескольких похожих, но не идентичных формах (данные против нового типа, стиля записи против обычных конструкторов и т. д.). Писать скучно и многократно, достаточно сложно, чтобы не быть механическим. [Предложение о реформе] [2] решает некоторые из этих вопросов (делая цитаты более применимыми).
Я также согласен с этим, однако, как отмечают некоторые из комментариев в «Новых направлениях для TH», отсутствие хороших готовых цитат из AST не является критическим недостатком. В этом пакете WIP я пытаюсь решить эти проблемы в виде библиотеки: https://github.com/mgsloan/quasi-extras . До сих пор я допускаю сращивание в нескольких местах, чем обычно, и могу сопоставлять шаблоны на AST.
- Сценическое ограничение - ад. Неспособность объединить функции, определенные в одном и том же модуле, является меньшей его частью: другое следствие - если у вас есть объединение верхнего уровня, все, что находится после него в модуле, будет вне области действия чего-либо до него. Другие языки с этим свойством (C, C ++) делают его работоспособным, позволяя вам перенаправлять объявления, но Haskell этого не делает. Если вам нужны циклические ссылки между объединенными объявлениями или их зависимостями и зависимостями, вы обычно просто испорчены.
Я столкнулся с проблемой невозможности определения циклического TH раньше ... Это довольно раздражает. Есть решение, но оно уродливо - оберните вещи, связанные с циклической зависимостью, в выражение TH, которое объединяет все сгенерированные объявления. Одним из таких генераторов объявлений может быть просто квазиквотер, который принимает код на Haskell.
- Это беспринципно. Под этим я подразумеваю, что в большинстве случаев, когда вы выражаете абстракцию, за этой абстракцией стоит какой-то принцип или концепция. Для многих абстракций принцип, лежащий в их основе, может быть выражен в их типах. Когда вы определяете класс типов, вы часто можете сформулировать законы, которым должны подчиняться экземпляры и которые могут принимать клиенты. Если вы используете [новую универсальную функцию] GHC [3], чтобы абстрагировать форму объявления экземпляра над любым типом данных (в пределах границ), вы скажете: «для типов суммы это работает так, для типов продукта - так ». Но Template Haskell - это просто тупые макросы. Это не абстракция на уровне идей, а абстракция на уровне AST, что лучше, но скромно, чем абстракция на уровне простого текста.
Это только беспринципно, если вы делаете с ним беспринципные вещи. Единственное отличие состоит в том, что с компилятором реализованы механизмы для абстракции, у вас больше уверенности в том, что абстракция не протекает. Возможно, демократизация языкового дизайна звучит немного страшно! Создатели библиотек TH должны хорошо документировать и четко определять значение и результаты инструментов, которые они предоставляют. Хорошим примером принципиального TH является производный пакет: http://hackage.haskell.org/package/derive - он использует DSL, так что пример многих дериваций / указывает / фактический деривация.
- Это связывает вас с GHC. Теоретически другой компилятор мог бы реализовать это, но на практике я сомневаюсь, что это когда-нибудь случится. (Это в отличие от различных расширений системы типов, которые, хотя они могут быть реализованы только GHC на данный момент, я легко мог представить себе, что они будут приняты другими компиляторами в будущем и в конечном итоге стандартизированы.)
Это очень хороший момент - TH API довольно большой и неуклюжий. Реализация кажется, что это может быть сложно. Однако есть только несколько способов решить проблему представления AST на Haskell. Я полагаю, что копирование TH ADTs и написание конвертера во внутреннее представление AST поможет вам в этом. Это было бы эквивалентно (что немаловажно) усилиям по созданию haskell-src-meta. Это также можно было бы просто повторно реализовать, просто распечатав TH AST и используя внутренний синтаксический анализатор компилятора.
Хотя я могу ошибаться, я не вижу в TH сложность расширения компилятора с точки зрения реализации. Это на самом деле одно из преимуществ «сохранения простоты» и отсутствия фундаментального уровня в некоторой теоретически привлекательной, статически проверяемой шаблонной системе.
- API не стабилен. Когда в GHC добавляются новые языковые функции, а пакет template-haskell обновляется для их поддержки, это часто связано с несовместимыми с обратным характером изменениями типов данных TH. Если вы хотите, чтобы ваш код TH был совместим с более чем одной версией GHC, вам нужно быть очень осторожным и, возможно, использовать его
CPP
.
Это тоже хороший момент, но несколько драматичный. Хотя в последнее время были добавлены API, они не вызывали частых поломок. Кроме того, я думаю, что благодаря вышеприведенному цитированию AST, о котором я упоминал ранее, API, который фактически должен использоваться, может быть существенно сокращен. Если никакое конструирование / сопоставление не требует отдельных функций и вместо этого выражается в виде литералов, то большая часть API исчезает. Более того, код, который вы пишете, будет проще переносить в представления AST для языков, похожих на Haskell.
Подводя итог, я думаю, что TH - это мощный инструмент, которым почти не уделяется должного внимания. Меньшее количество ненависти может привести к созданию более оживленной экосистемы библиотек, способствующей внедрению большего количества прототипов языковых функций. Было замечено, что TH - это мощный инструмент, который может позволить вам / делать / почти все. Анархия! Что ж, по моему мнению, эта мощь может позволить вам преодолеть большинство ее ограничений и создавать системы, способные к совершенно принципиальным подходам метапрограммирования. Стоит использовать уродливые хаки для имитации «правильной» реализации, поскольку таким образом дизайн «правильной» реализации постепенно станет понятен.
В моей личной идеальной версии нирваны, большая часть языка фактически переместилась бы из компилятора в библиотеки этого разнообразия. Тот факт, что функции реализованы в виде библиотек, не сильно влияет на их способность точно абстрагироваться.
Каков типичный ответ Хаскелла на стандартный код? Абстракция. Какие наши любимые абстракции? Функции и классы типов!
Классы типов позволяют нам определить набор методов, которые затем могут использоваться во всех видах функций, общих для этого класса. Тем не менее, кроме этого, единственный способ, которым классы помогают избежать шаблонов, заключается в предложении «определений по умолчанию». Теперь вот пример беспринципной возможности!
Наборы минимальных привязок не объявляются / проверяется компилятором. Это может привести к непреднамеренным определениям, дающим основание из-за взаимной рекурсии.
Несмотря на большое удобство и мощь, которые это дает, вы не можете указать значения по умолчанию для суперкласса, поскольку экземпляры-сироты http://lukepalmer.wordpress.com/2009/01/25/a-world-without-orphans/ Это позволит нам исправить числовая иерархия изящно!
Следование TH-подобным возможностям по умолчанию для методов привело к http://www.haskell.org/haskellwiki/GHC.Generics . Хотя это классная штука, мой единственный опыт отладки кода с использованием этих обобщений был почти невозможен из-за размера типа индуцированного для и ADT, такого сложного, как AST. https://github.com/mgsloan/th-extra/commit/d7784d95d396eb3abdb409a24360beb03731c88c
Другими словами, это пошло после функций, предоставляемых TH, но ему пришлось поднять всю область языка, язык конструирования, в представление системы типов. Хотя я вижу, что это хорошо работает для вашей общей проблемы, для сложных, кажется, она склонна приносить кучу символов, гораздо более ужасающих, чем хакерские игры TH.
TH дает вам вычисление выходного кода во время компиляции на уровне значений, в то время как generics заставляет вас поднять часть кода, соответствующую шаблонам / рекурсии, в систему типов. Хотя это ограничивает пользователя несколькими довольно полезными способами, я не думаю, что сложность того стоит.
Я думаю, что отказ от TH и метапрограммирование, подобное lisp, привело к предпочтению таких вещей, как метод-значения по умолчанию, а не к более гибкому, макро-расширению, как объявления экземпляров. Дисциплина избегания вещей, которые могут привести к непредвиденным результатам, является мудрой, однако, мы не должны игнорировать то, что способная система типов Хаскелла допускает более надежное метапрограммирование, чем во многих других средах (путем проверки сгенерированного кода).