Що так погано в шаблоні Haskell?


252

Здається, що шаблон Haskell часто розглядається громадою Haskell як прикрою зручністю. Важко сказати словами саме те, що я спостерігав у зв'язку з цим, але розглянемо ці кілька прикладів

Я бачив різні дописи в блогах, де люди роблять досить акуратні речі із шаблоном Haskell, завдяки чому гарніший синтаксис, який просто не був би можливим у звичайному Haskell, а також надзвичайне скорочення шаблонів. То чому так виглядає Шаблон Хаскелл таким чином? Що робить це небажаним? За яких обставин слід уникати шаблону Haskell і чому?


56
Я не згоден з голосуванням за рух; Я задаю це запитання в тому ж дусі, що я запитав: Що так поганого в ледачому вході / виході? і я очікую побачити відповіді такої ж моди. Я відкритий для переформулювання питання, чи допоможе це.
Ден Бертон

51
@ErikPhilips Чому ви не даєте людям, які часто користуються цим тегом, вирішувати, чи він тут належить? Схоже, ваша єдина взаємодія із спільнотою Haskell - це задавати її питання.
Габріель Гонсалес

5
@GabrielGonzalez очевидно, що з поточного питання і відповіді, що він не відповідає FAQ, під яким типом питання я не повинен тут задавати: актуальної проблеми не вирішувати . Питання не має вирішеної конкретної проблеми з кодом і має лише концептуальний характер. І виходячи з питання, чи слід уникати шаблону haskell, він також потрапляє в переповнення стека - це не механізм рекомендацій .
Ерік Філіпс

27
@ErikPhilips Аспект Рекомендаційного механізму не має відношення до цього питання, оскільки ви помітите, що таке стосується рішення між різними інструментами (наприклад, "скажіть, якою мовою я повинен користуватися"). На відміну від мене, я просто прошу пояснення щодо Шаблону Haskell конкретно, і FAQ задає "якщо ваша мотивація" я хотів би, щоб інші пояснили мені [пусте] ", значить, ви, мабуть, добре". Порівняйте, наприклад, GOTO як і раніше вважається шкідливим?
Ден Бертон

29
Голосування за повторне відкриття. Тільки тому, що це питання вищого рівня, не означає, що він не гарний.
Дьорджі Андрасек

Відповіді:


171

Однією з причин уникнення шаблону Haskell є те, що він в цілому не є безпечним для всіх типів, що суперечить більшості "духу Haskell". Ось кілька прикладів цього:

  • Ви не маєте контролю над тим, який тип Haskell AST генерує фрагмент коду TH за межами місця, де він з’явиться; у вас може бути значення типу Exp, але ви не знаєте, чи це вираз, який представляє a [Char]чи a (a -> (forall b . b -> c))чи що. TH було б більш надійним, якби можна було виразити, що функція може генерувати лише вирази певного типу, або лише декларації функції, або лише шаблони відповідності конструктора даних тощо.
  • Ви можете створювати вирази, які не компілюються. Ви створили вираз, який посилається на вільну змінну foo, яка не існує? Нелегка удача, ви побачите лише те, що фактично використовуєте генератор коду, і лише за тих обставин, які викликають генерацію саме цього коду. Тест на одиницю теж важко.

TH також відверто небезпечний:

  • Код, який працює під час компіляції, може робити довільне IO, включаючи запуск ракет або крадіжку вашої кредитної картки. Вам не хочеться переглядати кожен пакет кабалів, який ви завантажуєте в пошуках ТО подвигів.
  • TH може отримати доступ до функцій та визначень модулів-приватних, повністю порушуючи інкапсуляцію в деяких випадках.

Тоді є деякі проблеми, які роблять TH функції менш цікавими для використання як розробник бібліотеки:

  • TH-код не завжди може бути компонованим. Скажімо, хтось робить генератор для лінз, і частіше за все цей генератор буде структурований таким чином, що його можна буде викликати тільки безпосередньо "кінцевим користувачем", а не іншим TH-кодом, наприклад, приймаючи список конструкторів типів для генерації лінз для параметра. Створити цей список у коді складно, тоді як користувачеві залишається лише писати generateLenses [''Foo, ''Bar].
  • Розробники навіть не знають, що TH-код може бути складений. Ви знали, що можете писати forM_ [''Foo, ''Bar] generateLens? Qє просто монадою, тому ви можете використовувати всі звичні функції на ній. Деякі люди цього не знають, і через це вони створюють кілька перевантажених версій по суті одних і тих же функцій з однаковою функціональністю, і ці функції призводять до певного ефекту роздуття. Крім того, більшість людей пишуть своїх генераторів у Qмонаді навіть тоді, коли цього не потрібно, це як писати bla :: IO Int; bla = return 3; ви надаєте функції більше "оточення", ніж це потрібно, і клієнти функції зобов'язані забезпечити це середовище як ефект цього.

Нарешті, є деякі речі, які роблять TH функції менш цікавими для використання як кінцевого користувача:

  • Непрозорість. Якщо функція TH має тип Q Dec, вона може генерувати абсолютно все, що знаходиться на верхньому рівні модуля, і ви абсолютно не маєте контролю над тим, що буде створено.
  • Монолітизм. Ви не можете контролювати, скільки створюється функція TH, якщо розробник не дозволяє; якщо ви знайдете функцію, яка генерує інтерфейс бази даних та інтерфейс серіалізації JSON, ви не можете сказати "Ні, я хочу лише інтерфейс бази даних, дякую; я прокатую свій власний інтерфейс JSON"
  • Час виконання. TH код займає досить багато часу для запуску. Код інтерпретується заново кожен раз, коли складається файл, і часто для запущеного TH-коду потрібна маса пакетів, які потрібно завантажити. Це значно сповільнює час компіляції.

4
Додайте до цього той факт, що шаблон-haskell історично був дуже погано задокументований. (Хоча я просто переглянув ще раз, і здається, що зараз принаймні дещо кращі.) Також, щоб зрозуміти шаблон-haskell, ви, по суті, повинні зрозуміти граматику мови Haskell, яка накладає певну складність (це не схема). Ці дві речі сприяли мені навмисно, не намагаючись зрозуміти TH, коли я був початківцем Haskell.
mightybyte

14
Не забувайте, що використання шаблону Haskell неодмінно означає порядок декларацій має значення! TH просто не інтегрується настільки тісно, ​​як можна сподіватися, враховуючи рівний лак Haskell (будь то 1.4, ’98, 2010 чи навіть Глазго).
Thomas M. DuBuisson

13
Ви можете міркувати про Haskell без особливих труднощів, немає такої гарантії щодо Шаблону Haskell.
серпень

15
А що сталося з обіцянкою Олега щодо безпечної для людей альтернативи TH? Я маю на увазі його роботу, засновану на його роботі "Нарешті беззмістовий, частково оцінений" та інших його замітках тут . Це виглядало настільки перспективно, коли вони оголосили про це, і тоді я ніколи не чув про це іншого слова.
Габріель Гонсалес


53

Це виключно моя власна думка.

  • Це некрасиво використовувати. $(fooBar ''Asdf)просто не виглядає приємно. Поверхневий, звичайно, але це сприяє.

  • Ще пишніше писати. Цитування спрацьовує іноді, але багато часу доводиться робити ручним прищепленням та сантехнікою AST. API є великим і громіздким, завжди є багато випадків , ви не дбаєте про те, але все ж потрібно відправки, і випадки , які ви робите турботу про , як правило, присутня в декількох подібних , але не ідентичних форм (дані проти Newtype, запис -стиль проти звичайних конструкторів тощо). Писати нудно і повторюється досить складно, щоб не бути механічним. Пропозиція щодо реформи стосується деякого з цього (робить цитати більш застосовними).

  • Етапне обмеження - пекло. Неможливо спланувати функції, визначені в одному модулі, є його меншою частиною: інший наслідок - якщо у вас є сплайс верхнього рівня, все після нього в модулі вийде за межі будь-якого раніше. Інші мови з цією властивістю (C, C ++) роблять її працездатною, дозволяючи пересилати декларування речей, але Haskell цього не робить. Якщо вам потрібні циклічні посилання між з'єднаними деклараціями або їх залежностями та залежними, зазвичай вас просто накручують.

  • Це недисципліновано. Я маю на увазі під цим те, що більшість часу, коли ви висловлюєте абстракцію, за цією абстракцією стоїть якийсь принцип або концепція. Для багатьох абстракцій принцип, що стоїть за ними, може бути виражений у їх типах. Для типів класів ви можете часто формулювати закони, яким повинні дотримуватися екземпляри, і клієнти можуть їх припускати. Якщо ви використовуєте нову генеричну функцію GHC, щоб абстрагувати форму декларації екземпляра над будь-яким типом даних (у межах), ви скажете "для типів суми, це працює так, для типів продуктів, він працює так". Шаблон Haskell, з іншого боку, просто макроси. Це не абстракція на рівні ідей, а абстракція на рівні AST, що краще, але лише скромно, ніж абстракція на рівні простого тексту. *

  • Це пов'язує вас з GHC. Теоретично інший компілятор міг би його реалізувати, але на практиці я сумніваюся, що це коли-небудь станеться. (Це на відміну від розширень системи різних типів, які, хоча вони можуть бути реалізовані лише GHC на даний момент, я можу легко уявити, що їх прийняли інші компілятори в дорозі і врешті-решт стандартизували.)

  • API не стабільний. Коли до GHC додаються нові мовні функції та оновляється пакет шаблонів haskell для їх підтримки, це часто включає зміни, несумісні із змінами типів даних TH. Якщо ви хочете, щоб ваш TH-код був сумісний з більш ніж однією версією GHC, вам потрібно бути дуже обережним і, можливо, використовувати CPP.

  • Існує загальний принцип, що ви повинні використовувати правильний інструмент для роботи та найменший, який буде достатній, і в цій аналогії Шаблон Haskell - це щось подібне . Якщо є спосіб зробити це не Шаблоном Haskell, загалом краще.

Перевага шаблону Haskell полягає в тому, що ви можете робити те, що ви не могли зробити іншим способом, і це великий. Більшу частину часу речі, для яких використовується TH, можна було б зробити інакше, лише якщо вони були реалізовані безпосередньо як функції компілятора. TH надзвичайно вигідно мати і те, що дозволяє вам робити це, і тому, що дозволяє прототипувати потенційні розширення компілятора набагато більш легким і багаторазовим способом (див., Наприклад, різні пакети об'єктивів).

Підводячи підсумок, чому я думаю, що до Шаблону Хаскелла є негативні почуття: він вирішує багато проблем, але для будь-якої задачі, яку він вирішує, відчувається, що має бути краще, більш елегантне, дисципліноване рішення, яке краще підходить для вирішення цієї проблеми, той, який не вирішує проблему шляхом автоматичної генерації котлоагрегату, а шляхом зняття необхідності мати котельну плиту.

* Хоча я часто відчуваю, що CPPкращі співвідношення потужності та ваги для тих проблем, які він може вирішити.

РЕДАКЦІЯ 23-04-14: Те, що я часто намагався досягти у вищевикладеному, і лише нещодавно з’ясувалося саме так, - це те, що важливе розмежування між абстракцією та дедуплікацією. Правильна абстракція часто призводить до дедупликації як побічного ефекту, а дублювання часто є показовою ознакою неадекватної абстракції, але це не є причиною, чому вона цінна. Правильна абстракція - це те, що робить код правильним, зрозумілим та досяжним. Дедуплікація лише робить її коротшою. Шаблон Haskell, як і макроси загалом, є інструментом дедуплікації.


"CPP має кращі співвідношення потужності та ваги для тих проблем, які він може вирішити". Справді. І в C99 він може вирішити все, що завгодно. Розглянемо такі: COS , Chaos . Я також не розумію, чому люди вважають, що покоління AST краще. Це просто більше неоднозначності та менш ортогональності щодо інших мовних особливостей
Бріттон Керін

31

Я хотів би розглянути декілька моментів, які викладає dflemstr.

Я не знаходжу того факту, що ви не можете набрати перевірку TH, щоб це хвилювало. Чому? Тому що навіть якщо є помилка, це все одно буде час компіляції. Я не впевнений, чи це посилює мій аргумент, але це по духу схоже на помилки, які ви отримуєте під час використання шаблонів у C ++. Я думаю, що ці помилки зрозуміліші, ніж помилки C ++, оскільки ви отримаєте гарненько роздруковану версію згенерованого коду.

Якщо вираз TH / квазі-цитата робить щось настільки вдосконалене, що хитрі кути можуть приховати, то, можливо, це необдумано?

Я трохи порушую це правило з квазі-котируваннями, над якими працював останнім часом (використовуючи haskell-src-exts / meta) - https://github.com/mgsloan/quasi-extras/tree/master/examples . Я знаю, що це вносить деякі помилки, такі як неможливість сплайсингу в узагальненому розумінні списку. Однак я думаю, що є хороший шанс, що деякі ідеї в http://hackage.haskell.org/trac/ghc/blog/Template%20Haskell%20Proposed потраплять у компілятор. До цього часу бібліотеки для розбору Haskell до TH дерев є майже ідеальним наближенням.

Що стосується швидкості / залежності компіляції, ми можемо використовувати пакет "нуль", щоб вбудувати створений код. Це принаймні приємно для користувачів певної бібліотеки, але ми не можемо зробити кращого у випадку редагування бібліотеки. Чи можуть залежності ТН роздувати породжені бінарні файли? Я думав, що це випустило все, на що не посилається складений код.

Обмеження / розщеплення етапів компіляції модуля Haskell не вдається.

RE Opacity: Це те саме для будь-якої функції бібліотеки, яку ви телефонуєте. Ви не маєте контролю над тим, що робитиме Data.List.groupBy. Ви просто маєте розумну "гарантію" / умову про те, що номери версій говорять вам про сумісність. Це дещо інше питання зміни, коли.

Ось де використання нуля окупається - ви вже оновлюєте згенеровані файли - тож ви завжди будете знати, коли форма сформованого коду змінилася. Дивлячись на diffs, можливо, трохи поважно, але для великої кількості генерованого коду, тож це одне місце, де кращий інтерфейс для розробників був би корисний.

RE Монолітизм: Ви, звичайно, можете обробити результати виразу TH, використовуючи власний код часу компіляції. Фільтрувати за типом / іменем декларації верхнього рівня було б не дуже багато. Чорт забирай, ти можеш уявити собі написання функції, яка робить це загалом. Для модифікації / демонолітизації квазіквітераторів ви можете вирівняти узор на "QuasiQuoter" і витягнути використовувані перетворення або зробити нове з точки зору старого.


1
Щодо непрозорості / монолітизму: Ви, звичайно, можете проходити [Dec]та видаляти речі, які вам не потрібні, але скажімо, що функція зчитує файл зовнішнього визначення при генерації інтерфейсу JSON. Просто тому, що ви не використовуєте те, Decщо не змушує генератора перестати шукати файл визначення, що робить компіляцію невдалою. З цієї причини було б непогано мати більш обмежену версію Qмонади, яка дозволила б генерувати нові імена (і такі речі), але не дозволяти IO, щоб, як ви кажете, можна було фільтрувати її результати / робити інші композиції .
dflemstr

1
Я погоджуюсь, має бути версія, що не стосується IO, Q / котирування! Це також допоможе вирішити - stackoverflow.com/questions/7107308/… . Після того, як ви переконаєтесь, що код часу компіляції не робить нічого небезпечного, здається, що ви можете просто запустити перевірку безпеки на отриманий код і переконатися, що він не посилається на приватні речі.
mgsloan

1
Ви коли-небудь писали свій контраргумент? Ще чекаю на ваше обіцяне посилання. Для тих, хто шукає старий вміст цієї відповіді: stackoverflow.com/reitions/10859441/1 , а також для тих, хто може переглянути видалений вміст: stackoverflow.com/reitions/10913718/6
Ден Бертон

1
чи є більш сучасна версія нуля, ніж версія про злом? Це (та репортаж darcs) востаннє оновлено у 2009 році. Обидві копії не створюються з поточним ghc (7.6).
aavogt

1
@aavogt tgeeky працював над тим, щоб виправити це, але я не думаю, що він закінчив: github.com/technogeeky/zeroth Якщо ніхто цього не зробить / зробить щось для цього, я зрештою з ним обійдуся.
mgsloan

16

Ця відповідь відповідає на питання, поставлені ілліссієм, точка за пунктом:

  • Це некрасиво використовувати. $ (fooBar '' Asdf) просто не виглядає приємно. Поверхневий, звичайно, але це сприяє.

Я згоден. Я відчуваю, що $ () був обраний так, щоб він виглядав як частина мови - використовуючи звичний піддон символів Haskell. Однак це саме те, що ви / не / бажаєте в символах, використовуваних для вашої макроскладки. Вони безумовно поєднуються занадто багато, і цей косметичний аспект є досить важливим. Мені подобається вигляд {{}} для сплайсів, тому що вони досить візуально виразні.

  • Ще пишніше писати. Цитування спрацьовує іноді, але багато часу доводиться робити вручну прищеплення та сантехніки AST. [API] [1] є великим і громіздким, завжди багато справ, які вам не цікаві, але все одно потрібно відправляти, а справи, які вас цікавлять, як правило, присутні в декількох подібних, але не однакових формах (дані порівняно з новим типом, стилем запису проти звичайних конструкторів тощо). Писати нудно і повторюється досить складно, щоб не бути механічним. [Пропозиція щодо реформи] [2] вирішує деякі з них (роблячи цитати більш широкими).

Я також погоджуюся з цим, однак, як зауважують деякі коментарі у "Нових напрямках для TH", відсутність хорошого котирування AST поза коробкою не є критичним недоліком. У цьому пакеті WIP я намагаюся вирішити ці проблеми в бібліотечній формі: https://github.com/mgsloan/quasi-extras . Поки що я дозволяю спланувати в ще декількох місцях, ніж зазвичай, і можу узгоджувати AST.

  • Етапне обмеження - пекло. Неможливо спланувати функції, визначені в одному модулі, є його меншою частиною: інший наслідок - якщо у вас є сплайс верхнього рівня, все після нього в модулі вийде за межі будь-якого раніше. Інші мови з цією властивістю (C, C ++) роблять її працездатною, дозволяючи пересилати декларування речей, але Haskell цього не робить. Якщо вам потрібні циклічні посилання між з'єднаними деклараціями або їх залежностями та залежними, зазвичай вас просто накручують.

Я зіткнувся з тим, що питання циклічних TH-визначень раніше було неможливим ... Це дуже дратує. Є рішення, але це некрасиво - оберніть речі, що беруть участь у циклічній залежності, у виразі TH, який поєднує всі створені декларації. Один з таких генераторів декларацій може бути просто квазі-котирувачем, який приймає код Haskell.

  • Це безпринципно. Я маю на увазі під цим те, що більшість часу, коли ви висловлюєте абстракцію, за цією абстракцією стоїть якийсь принцип або концепція. Для багатьох абстракцій принцип, що стоїть за ними, може бути виражений у їх типах. Коли ви визначаєте клас типу, ви можете часто формулювати закони, яким повинні дотримуватися екземпляри, і клієнти можуть їх припускати. Якщо ви використовуєте [нову функцію генерики] [3] GHC, щоб абстрагувати форму декларації екземпляра над будь-яким типом даних (у межах), ви скажете "для типів суми, це працює так, для типів продуктів, він працює так ". Але Шаблон Haskell - це просто німі макроси. Це не абстракція на рівні ідей, а абстракція на рівні AST, що краще, але лише скромно, ніж абстракція на рівні простого тексту.

Це безпринципне лише, якщо ви робите з ним безпринципні речі. Єдина відмінність полягає в тому, що з реалізованими компілятором механізмами абстракції ви маєте більше впевненості в тому, що абстракція не є герметичною. Можливо, демократизація мовного дизайну здається трохи страшною! Творцям бібліотек TH необхідно добре документувати та чітко визначати значення та результати інструментів, які вони надають. Хорошим прикладом принципового TH є пакет виведення: http://hackage.haskell.org/package/derive - він використовує DSL таким чином, що приклад багатьох похідних / вказує / фактичне виведення.

  • Це пов'язує вас з GHC. Теоретично інший компілятор міг би його реалізувати, але на практиці я сумніваюся, що це коли-небудь станеться. (Це на відміну від розширень системи різних типів, які, хоча вони можуть бути реалізовані лише GHC на даний момент, я можу легко уявити, що їх прийняли інші компілятори в дорозі і врешті-решт стандартизували.)

Це досить хороший момент - TH API досить великий і незграбний. Повторне впровадження здається, що це може бути важко. Однак існує лише лише кілька способів вирішити проблему представлення Haskell AST. Я думаю, що копіювання TH-ADT та написання перетворювача у внутрішнє представлення AST дозволить вам досягти великого шляху. Це було б рівнозначно (не незначним) зусиллям створення haskell-src-meta. Це також може бути просто реалізовано за допомогою гарного друку TH AST та використання внутрішнього аналізатора компілятора.

Хоча я можу помилятися, я не бачу TH, що є складним розширенням компілятора, з точки зору реалізації. Це фактично одна з переваг "простоти" та відсутності фундаментального шару бути якоюсь теоретично привабливою, статично перевіряється системою шаблонів.

  • API не стабільний. Коли до GHC додаються нові мовні функції та оновляється пакет шаблонів haskell для їх підтримки, це часто включає зміни, несумісні із змінами типів даних TH. Якщо ви хочете, щоб ваш TH-код був сумісний з більш ніж однією версією GHC, вам потрібно бути дуже обережним і, можливо, використовувати CPP.

Це теж хороший момент, але дещо драматизований. Незважаючи на те, що останнім часом були доповнення API, вони не мали великого рівня злому. Крім того, я вважаю, що при вищенаведеному цитуванні AST, про який я згадував раніше, API, який насправді потрібно використовувати, може бути дуже істотно скорочений. Якщо жодна побудова / відповідність не потребує чітких функцій, а замість цього виражається у формі літералів, то більша частина API зникає. Більше того, написаний вами код буде легше переноситись на представлення AST для мов, схожих на Haskell.


Підводячи підсумок, я вважаю, що TH - це потужний, напівзанедбаний інструмент. Менша ненависть може призвести до більш живої екосистеми бібліотек, що сприятиме впровадженню більше мовних прототипів функцій. Помічено, що TH - це дуже потужний інструмент, який дозволяє вам / робити / майже все. Анархія! Ну, на мою думку, ця потужність може дозволити вам подолати більшість його обмежень і побудувати системи, здатні досить принципово підходи метапрограмування. Варто використовувати некрасиві хаки для імітації «належної» реалізації, оскільки таким чином конструкція «належної» реалізації поступово стане зрозумілою.

У моїй особистій ідеальній версії нірвани значна частина мови фактично перейшла б із компілятора, в бібліотеки такого різноманіття. Те, що функції реалізовані як бібліотеки, не сильно впливає на їх здатність вірно абстрагуватися.

Яка типова відповідь Haskell на код котла? Абстракція. Які наші улюблені абстракції? Функції та типові класи!

Класи типу дозволяють нам визначити набір методів, які потім можуть бути використані у всіх функціях, загальних для цього класу. Однак, окрім цього, єдиний спосіб, що допомагає уникнути котлована, пропонує "визначення за замовчуванням". Тепер ось приклад безпринципної функції!

  • Мінімальні множини прив'язки не підлягають декларуванню / компілятор можна перевірити. Це може призвести до ненавмисних визначень, які дають нижнє значення через взаємної рекурсії.

  • Незважаючи на велику зручність та потужність, які це дасть, ви не можете вказати параметри за замовчуванням для суперкласових класів через випадки сиріт 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-подібного метапрограмування призвело до переваги таких речей, як метод за замовчуванням, а не до більш гнучких, макророзширень, як декларацій екземплярів. Дисципліна уникання речей, які можуть призвести до непередбачених результатів, є мудрою, однак, ми не повинні ігнорувати, що здатна система Haskell забезпечує більш надійну метапрограмування, ніж у багатьох інших середовищах (перевіряючи створений код).


4
Ця відповідь не стоїть сама по собі: ви робите купу посилань на іншу відповідь, яку я повинен зайти і знайти, перш ніж я зможу правильно прочитати вашу.
Бен Міллвуд

Це правда. Я намагався дати зрозуміти, про що говорять. Можливо, я відредагую, щоб викласти моменти ілюзіїв.
mgsloan

Я мушу сказати, що, можливо, "безпринципне" є сильнішим словом, ніж я мав би використовувати, з деякими конотаціями, які я не мав наміру - це, звичайно, не недобросовісно! Ця куля була тією, з якою я мав найбільше проблем, тому що у мене таке почуття чи неформована ідея в голові і проблеми з її висловленням, а "безпринципне" було одним із слів, які я зрозумів, що було десь поблизу. "Дисциплінований", напевно, ближче. Не маючи чіткої та чіткої формулювання, я натомість подав кілька прикладів. Якщо хтось може мені більш чітко пояснити, що я, мабуть, мав на увазі, я би оцінив це!
glaebhoerl

(Також я думаю, що ви, можливо, змінили обмеження для постановки та
нецензурні

1
До! Дякуємо, що вказали на це! Виправлено. Так, недисциплінований, мабуть, був би кращим способом викласти це. Я думаю, що тут є різниця між "вбудованими", а тому "добре зрозумілими" механізмами абстрагування проти "спеціальних" або визначених користувачем механізмів абстракції. Я маю на увазі, що я маю на увазі те, що ви могли б визначити бібліотеку TH, яка реалізувала щось досить схоже на диспетчеризацію класів (хоча і під час компіляції не під час виконання)
mgsloan

8

Одна досить прагматична проблема шаблону Haskell полягає в тому, що він працює лише тоді, коли доступний інтерпретатор байт-кодів GHC, що не у всіх архітектурах. Отже, якщо ваша програма використовує Template Haskell або покладається на бібліотеки, які її використовують, вона не працюватиме на машинах з процесором ARM, MIPS, S390 або PowerPC.

Це актуально на практиці: git-annex - це інструмент, написаний Haskell, який має сенс працювати на машинах, що турбуються про зберігання; такі машини часто мають не-i386-процесори. Особисто я запускаю git- annex на NSLU 2 (32 Мбайт оперативної пам’яті, процесор 266 МГц; чи знаєте ви, що Haskell прекрасно працює на такому обладнанні?) Якщо він використовує Template Haskell, це неможливо.

(Ситуація з GHC щодо ARM в наші дні сильно покращується, і я думаю, що 7.4.2 навіть працює, але справа все ще залишається).


1
"Шаблон Haskell покладається на вбудований компілятор байт-коду та інтерпретатор GHC для запуску виразів зрощення". - haskell.org/ghc/docs/7.6.2/html/users_guide/…
Йоахім

2
ах, я що - ні, TH не буде працювати без інтерпретатора байт-коду, але це відмінно від (хоча це стосується) ghci. Я не був би здивований, якби було ідеальне співвідношення між доступністю ghci та доступністю інтерпретатора байт-коду, враховуючи, що ghci залежить від інтерпретатора байт-коду, але проблема полягає у відсутності інтерпретатора байт-коду, а не відсутності ghci конкретно.
muhmuhten

1
(до речі, ghci має забавно слабку підтримку інтерактивного використання TH. спробуйте ghci -XTemplateHaskell <<< '$(do Language.Haskell.TH.runIO $ (System.Random.randomIO :: IO Int) >>= print; [| 1 |] )')
muhmuhten

Гаразд, з ghci я мав на увазі здатність GHC інтерпретувати (замість компілювати) код, незалежно від того, чи код надходить інтерактивно в двійковій системі ghci, або від TH сплайс.
Йоахім Брейтнер

6

Чому TH поганий? Для мене це зводиться до цього:

Якщо вам потрібно створити стільки повторюваних кодів, що ви намагаєтеся використовувати TH для його автоматичного генерування, ви робите це неправильно!

Подумай над цим. Половина привабливості Haskell полягає в тому, що його дизайн високого рівня дозволяє уникнути величезної кількості марного кодового коду, який вам доведеться писати іншими мовами. Якщо вам потрібна генерація коду за часом компіляції, ви в основному говорите, що ні ваша мова, ні дизайн вашої програми не вийшли з ладу. А ми, програмісти, не любимо провалюватися.

Іноді, звичайно, це потрібно. Але іноді ви можете уникнути необхідності TH, просто бути трохи розумнішими зі своїми проектами.

(Інша справа, що TH досить низького рівня. Немає грандіозного дизайну високого рівня; відкрито багато деталей внутрішнього впровадження GHC. І це робить API схильним до змін ...)


Я не думаю, що це означає, що ваша мова чи програма зазнала невдачі, особливо якщо ми розглянемо QuasiQuotes. Що стосується синтаксису, завжди є компроміси. Деякі синтаксиси краще описують певний домен, тому ви хочете іноді мати можливість перейти до іншого синтаксису. QuasiQuotes дозволяють елегантно перемикатися між синтаксисами. Це дуже потужно і його використовують Yesod та інші додатки. Вміти писати код покоління HTML за допомогою синтаксису, який здається, що HTML - це дивовижна особливість.
CoolCodeBro

@CoolCodeBro Так, квазіцитування є досить приємним, і я думаю, що TH трохи ортогональне. (Очевидно, що це реалізовано поверх TH.) Я думав більше про те, щоб використовувати TH для генерації екземплярів класу, або для побудови функцій декількох артій, або щось подібне.
MathematicalOrchid
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.