Jawaban ini adalah jawaban atas masalah yang dikemukakan oleh illissius, poin demi poin:
- Ini jelek untuk digunakan. $ (fooBar '' Asdf) tidak terlihat bagus. Dangkal, tentu, tetapi berkontribusi.
Saya setuju. Saya merasa $ () dipilih agar terlihat seperti bagian dari bahasa - menggunakan palet simbol Haskell yang sudah dikenal. Namun, itulah yang Anda / tidak / inginkan dalam simbol yang digunakan untuk penyambungan makro Anda. Mereka pasti berbaur terlalu banyak, dan aspek kosmetik ini sangat penting. Saya suka tampilan {{}} untuk splices, karena keduanya sangat berbeda secara visual.
- Bahkan lebih buruk untuk menulis. Mengutip kadang-kadang berhasil, tetapi sering kali Anda harus melakukan pencangkokan dan pemipaan AST manual. [API] [1] besar dan berat, selalu ada banyak kasus yang tidak Anda pedulikan tetapi masih perlu dikirim, dan kasus yang Anda pedulikan cenderung hadir dalam berbagai bentuk yang serupa tetapi tidak identik (data) vs tipe baru, gaya rekaman vs konstruktor normal, dan sebagainya). Membosankan dan berulang-ulang untuk menulis dan cukup rumit untuk tidak mekanis. [Proposal reformasi] [2] membahas beberapa hal ini (membuat penawaran lebih luas berlaku).
Saya juga setuju dengan ini, namun, seperti yang diperhatikan oleh beberapa komentar di "Petunjuk Arah Baru untuk TH", kurangnya kutipan AST yang bagus di luar kotak bukanlah cacat kritis. Dalam paket WIP ini, saya berupaya mengatasi masalah ini dalam bentuk pustaka: https://github.com/mgsloan/quasi-extras . Sejauh ini saya mengizinkan splicing di beberapa tempat lebih banyak dari biasanya dan dapat mencocokkan pola pada AST.
- Pembatasan panggung adalah neraka. Tidak dapat memisahkan fungsi-fungsi yang didefinisikan dalam modul yang sama adalah bagian yang lebih kecil darinya: konsekuensi lainnya adalah jika Anda memiliki sambungan tingkat atas, semua yang ada di dalam modul akan berada di luar ruang lingkup apa pun sebelumnya. Bahasa lain dengan properti ini (C, C ++) membuatnya bisa diterapkan dengan memungkinkan Anda untuk meneruskan mendeklarasikan sesuatu, tetapi Haskell tidak. Jika Anda memerlukan referensi siklik antara deklarasi yang disambungkan atau dependensi dan tanggungannya, Anda biasanya hanya kacau.
Saya pernah mengalami masalah definisi TH siklik menjadi tidak mungkin sebelumnya ... Ini sangat mengganggu. Ada solusi, tapi jelek - bungkus hal-hal yang terlibat dalam ketergantungan siklik dalam ekspresi TH yang menggabungkan semua deklarasi yang dihasilkan. Salah satu dari deklarasi generator ini bisa saja berupa quasi-quoter yang menerima kode Haskell.
- Itu tidak berprinsip. Yang saya maksud dengan ini adalah bahwa sebagian besar waktu ketika Anda mengekspresikan abstraksi, ada semacam prinsip atau konsep di balik abstraksi itu. Untuk banyak abstraksi, prinsip di belakangnya dapat dinyatakan dalam tipenya. Ketika Anda mendefinisikan kelas tipe, Anda sering dapat merumuskan undang-undang mana yang harus dipatuhi oleh instance dan klien dapat berasumsi. Jika Anda menggunakan GHC [fitur generik baru] [3] untuk mengabstraksi bentuk deklarasi instance atas tipe data apa pun (dalam batas), Anda bisa mengatakan "untuk tipe penjumlahan, kerjanya seperti ini, untuk tipe produk, berfungsi seperti itu ". Tapi Template Haskell hanyalah makro bodoh. Ini bukan abstraksi pada level ide, tetapi abstraksi pada level AST, yang lebih baik, tetapi hanya secara sederhana, daripada abstraksi pada level teks biasa.
Itu hanya tidak berprinsip jika Anda melakukan hal-hal yang tidak berprinsip dengannya. Satu-satunya perbedaan adalah bahwa dengan compiler menerapkan mekanisme untuk abstraksi, Anda lebih percaya bahwa abstraksi tidak bocor. Mungkin demokratisasi desain bahasa memang terdengar agak menakutkan! Pencipta perpustakaan TH perlu mendokumentasikan dengan baik dan jelas mendefinisikan arti dan hasil dari alat yang mereka berikan. Sebuah contoh yang baik dari berprinsip TH adalah paket turunan : http://hackage.haskell.org/package/derive - ia menggunakan DSL sehingga contoh dari banyak derivasi / menentukan / derivasi yang sebenarnya.
- Itu mengikat Anda ke GHC. Secara teori kompiler lain dapat mengimplementasikannya, tetapi dalam praktiknya saya ragu ini akan pernah terjadi. (Ini berbeda dengan berbagai jenis ekstensi sistem yang, meskipun mungkin hanya diimplementasikan oleh GHC saat ini, saya dapat dengan mudah membayangkan diadopsi oleh kompiler lain di ujung jalan dan akhirnya distandarisasi.)
Itu poin yang cukup bagus - API TH cukup besar dan kikuk. Mengimplementasikan ulang sepertinya bisa jadi sulit. Namun, hanya ada beberapa cara untuk mengiris masalah mewakili ASK Haskell. Saya membayangkan bahwa menyalin ADT TH, dan menulis konverter ke representasi AST internal akan membuat Anda mendapatkan banyak jalan di sana. Ini akan setara dengan upaya (tidak signifikan) untuk menciptakan haskell-src-meta. Ini juga bisa diimplementasikan kembali dengan mencetak THT AST dan menggunakan parser internal kompiler.
Walaupun saya bisa saja salah, saya tidak melihat TH sebagai ekstensi kompiler yang rumit, dari perspektif implementasi. Ini sebenarnya salah satu manfaat dari "menjaganya tetap sederhana" dan tidak memiliki lapisan dasar menjadi beberapa sistem templating yang secara teoritis menarik dan dapat diverifikasi secara statis.
- API tidak stabil. Ketika fitur bahasa baru ditambahkan ke GHC dan paket template-haskell diperbarui untuk mendukungnya, ini sering melibatkan perubahan yang tidak kompatibel ke belakang untuk tipe data TH. Jika Anda ingin kode TH Anda kompatibel dengan lebih dari satu versi GHC, Anda harus sangat berhati-hati dan mungkin menggunakannya
CPP
.
Ini juga poin yang bagus, tetapi agak dramatis. Meskipun ada penambahan API akhir-akhir ini, mereka belum menginduksi kerusakan secara luas. Juga, saya berpikir bahwa dengan kutipan AST superior yang saya sebutkan sebelumnya, API yang benar-benar perlu digunakan dapat dikurangi secara substansial. Jika tidak ada konstruksi / pencocokan membutuhkan fungsi yang berbeda, dan sebaliknya dinyatakan sebagai literal, maka sebagian besar API menghilang. Selain itu, kode yang Anda tulis akan port lebih mudah ke representasi AST untuk bahasa yang mirip dengan Haskell.
Singkatnya, saya berpikir bahwa TH adalah alat yang kuat, semi-diabaikan. Lebih sedikit kebencian dapat mengarah pada eko-sistem perpustakaan yang lebih hidup, mendorong penerapan prototipe fitur bahasa yang lebih banyak. Telah diamati bahwa TH adalah alat yang dikuasai, yang dapat membuat Anda / melakukan / hampir apa saja. Anarki! Ya, menurut saya kekuatan ini memungkinkan Anda untuk mengatasi sebagian besar keterbatasannya, dan membangun sistem yang mampu melakukan pendekatan meta-pemrograman yang cukup berprinsip. Sebaiknya gunakan hacks jelek untuk mensimulasikan implementasi yang "tepat", karena dengan cara ini desain implementasi yang "tepat" akan secara bertahap menjadi jelas.
Dalam versi nirwana ideal pribadi saya, banyak bahasa yang benar-benar akan keluar dari kompiler, ke perpustakaan-perpustakaan dari varietas ini. Fakta bahwa fitur diimplementasikan sebagai perpustakaan tidak banyak mempengaruhi kemampuan mereka untuk setia abstrak.
Apa jawaban khas Haskell untuk kode boilerplate? Abstraksi. Apa abstraksi favorit kita? Fungsi dan typeclasses!
Typeclasses mari kita mendefinisikan satu set metode, yang kemudian dapat digunakan dalam segala macam fungsi generik pada kelas itu. Namun, selain ini, satu-satunya cara kelas membantu menghindari boilerplate adalah dengan menawarkan "definisi default". Sekarang ini adalah contoh fitur yang tidak berprinsip!
Set pengikat minimal tidak dapat dideklarasikan / dikompilasi. Ini bisa mengarah pada definisi yang tidak sengaja yang menghasilkan bottom karena rekursi timbal balik.
Terlepas dari kemudahan dan kekuatan yang akan dihasilkan, Anda tidak dapat menentukan standar superclass, karena instance yatim http://lukepalmer.wordpress.com/2009/01/25/a-world-without-orphans/ Ini akan memungkinkan kami memperbaiki hierarki numerik dengan anggun!
Mengejar kemampuan seperti-TH untuk standar metode mengarah ke http://www.haskell.org/haskellwiki/GHC.Generics . Meskipun ini adalah hal yang keren, kode debugging satu-satunya pengalaman saya yang menggunakan generik ini hampir tidak mungkin, karena ukuran tipe yang diinduksi untuk dan ADT serumit AST. https://github.com/mgsloan/th-extra/commit/d7784d95d396eb3abdb409a24360beb03731c88c
Dengan kata lain, ini mengikuti fitur yang disediakan oleh TH, tetapi harus mengangkat seluruh domain bahasa, bahasa konstruksi, ke dalam representasi sistem tipe. Walaupun saya dapat melihatnya bekerja dengan baik untuk masalah umum Anda, untuk masalah yang kompleks, tampaknya cenderung menghasilkan tumpukan simbol yang jauh lebih mengerikan daripada peretasan TH.
TH memberi Anda perhitungan waktu-kompilasi tingkat-nilai dari kode output, sedangkan generik memaksa Anda untuk mengangkat bagian pencocokan pola / rekursi dari kode ke dalam sistem tipe. Meskipun ini membatasi pengguna dalam beberapa cara yang cukup berguna, saya tidak berpikir kompleksitasnya sepadan.
Saya berpikir bahwa penolakan TH dan metaprogramming seperti lisp mengarah ke preferensi terhadap hal-hal seperti metode-default daripada lebih fleksibel, ekspansi makro seperti deklarasi instance. Disiplin menghindari hal-hal yang dapat menyebabkan hasil yang tidak terduga adalah bijaksana, namun, kita tidak boleh mengabaikan bahwa sistem tipe Haskell yang memungkinkan memungkinkan metaprogramming yang lebih andal daripada di banyak lingkungan lain (dengan memeriksa kode yang dihasilkan).