Bu cevap, illissius'un gündeme getirdiği konulara yanıt niteliğindedir:
- Kullanmak çirkin. $ (fooBar '' Asdf) sadece hoş görünmüyor. Yüzeysel, tabii, ama katkıda bulunur.
Katılıyorum. $ () Dilin bir parçası gibi görünmek için seçilmiş gibi hissediyorum - Haskell'in tanıdık sembol paletini kullanarak. Bununla birlikte, makro birleştirme için kullanılan sembollerde tam olarak sizin / istemediğiniz / istemediğiniz şey budur. Kesinlikle çok fazla karışırlar ve bu kozmetik yönü oldukça önemlidir. Ekler için {{}} görünümünü seviyorum, çünkü görsel olarak oldukça farklılar.
- Yazmak daha çirkin. Alıntı yapmak bazen işe yarar, ancak çoğu zaman manuel AST aşılama ve sıhhi tesisat yapmak zorundasınız. [API] [1] büyük ve kullanışsızdır, her zaman umursadığınız ancak yine de göndermeniz gereken birçok durum vardır ve önem verdiğiniz vakalar, benzer ancak aynı olmayan birden çok formda (veri olarak) olma eğilimindedir vs newtype, record-style vs. normal yapıcıları vs.). Yazmak sıkıcı ve tekrarlayıcı ve mekanik olmayacak kadar karmaşık. [Reform önerisi] [2] bunlardan bazılarına değinmektedir (teklifleri daha geniş çapta uygulanabilir kılmaktadır).
Bununla birlikte, buna katılıyorum, ancak, "TH için Yeni Yönergeler" deki bazı yorumların gözlemlediği gibi, kutudan çıkan iyi AST teklifinin eksikliği kritik bir kusur değildir. Bu WIP paketinde şu sorunları kütüphane biçiminde çözmeye çalışıyorum: https://github.com/mgsloan/quasi-extras . Şimdiye kadar normalden birkaç yerde daha fazla eklemeye izin veriyorum ve AST'ler üzerinde eşleşme yapabilir.
- Sahne kısıtlaması cehennemdir. Aynı modülde tanımlanan işlevleri birleştirememek, onun daha küçük kısmıdır: diğer sonuç, üst düzey bir eklemeniz varsa, modülde bundan sonraki her şeyin, ondan önceki her şeyin kapsamı dışında olacağıdır. Bu özelliğe sahip diğer diller (C, C ++), işleri bildirmenize izin vererek çalışabilir hale getirir, ancak Haskell bunu yapmaz. Eklenmiş bildirimler veya bunların bağımlılıkları ve bağımlıları arasında döngüsel referanslara ihtiyacınız varsa, genellikle yoruldunuz.
Daha önce imkansız olan döngüsel TH tanımları meselesiyle karşılaştım ... Bu oldukça can sıkıcı. Bir çözüm var, ama çirkin - döngüsel bağımlılığa dahil olan şeyleri üretilen tüm bildirimleri birleştiren bir TH ifadesine sarın. Bu bildirim üreticilerinden biri, Haskell kodunu kabul eden bir yarı-quoter olabilir.
- İlkesiz. Bununla kastettiğim, çoğu zaman bir soyutlamayı ifade ettiğinizde, o soyutlamanın arkasında bir tür ilke veya kavram vardır. Birçok soyutlama için, bunların arkasındaki ilke türleriyle ifade edilebilir. Bir tür sınıfı tanımladığınızda, çoğu zaman hangi örneklere uyulması ve istemcilerin kabul edebileceği yasalar formüle edebilirsiniz. Herhangi bir veri türü (sınırlar içinde) üzerinde bir örnek bildirimi şeklinde soyutlamak için GHC'nin [yeni jenerikler özelliği] [3] 'nü kullanırsanız, "toplam türleri için, bu şekilde çalışır, ürün türleri için, şöyle çalışır ". Ancak Template Haskell sadece aptal makrolar. Fikirler düzeyinde soyutlama değil, düz metin seviyesinde soyutlamadan daha iyi, ama sadece mütevazı olan AST'ler düzeyinde soyutlamadır.
Sadece onunla ilkesiz şeyler yaparsanız ilkesizdir. Tek fark, derleyici soyutlama mekanizmaları ile uygulandığında, soyutlamanın sızıntılı olmadığına daha fazla güven duymanızdır. Belki de dil tasarımını demokratikleştirmek biraz korkutucu geliyor! TH kütüphanelerinin yaratıcılarının iyi belgelemeleri ve sağladıkları araçların anlamlarını ve sonuçlarını açıkça tanımlamaları gerekir. Prensip TH'nin iyi bir örneği türetme paketidir: http://hackage.haskell.org/package/derive - türetme / belirleme / gerçek türetme örneklerinin çoğunun örneği bir DSL kullanır.
- Sizi GHC'ye bağlar. Teorik olarak başka bir derleyici bunu uygulayabilir, ancak pratikte bunun olacağından şüpheliyim. (Bu, sadece şu anda sadece GHC tarafından uygulanabilecek olsa da, yolun altındaki diğer derleyiciler tarafından benimsenip nihayetinde standartlaştırılabilen çeşitli tür sistem uzantılarının aksine.)
Bu oldukça iyi bir nokta - TH API oldukça büyük ve hantal. Yeniden uygulamak zor gibi görünüyor. Bununla birlikte, Haskell AST'leri temsil etme problemini dilimlemenin sadece birkaç yolu vardır. TH ADT'leri kopyalamanın ve dahili AST temsilcisine bir dönüştürücü yazmanın size bu konuda iyi bir yol alacağını hayal ediyorum. Bu, haskell-src-meta oluşturma (önemsiz olmayan) çabasına eşdeğer olacaktır. TH AST'yi güzel bir şekilde yazdırarak ve derleyicinin dahili ayrıştırıcısını kullanarak da yeniden uygulanabilir.
Yanılıyor olsam da, TH'yi bir uygulama perspektifinden derleyici uzantısının karmaşıklığı olarak görmüyorum. Bu aslında "basit tutmanın" ve temel katmanın teorik olarak çekici, statik olarak doğrulanabilir bir şablonlama sistemi olmamasının faydalarından biridir.
- API sabit değil. GHC'ye yeni dil özellikleri eklendiğinde ve template-haskell paketi bunları destekleyecek şekilde güncellendiğinde, bu genellikle TH veri türlerinde geriye dönük uyumsuz değişiklikleri içerir. TH kodunuzun birden fazla GHC sürümü ile uyumlu olmasını istiyorsanız, çok dikkatli olmanız ve muhtemelen kullanmanız gerekir
CPP
.
Bu da iyi bir nokta, ama biraz dramatik. Son zamanlarda API eklemeleri yapılmış olsa da, büyük ölçüde kırılma indüklememiştir. Ayrıca, daha önce bahsettiğim üstün AST alıntısıyla, gerçekten kullanılması gereken API'nin çok önemli ölçüde azaltılabileceğini düşünüyorum. Hiçbir yapı / eşleştirme farklı işlevlere ihtiyaç duymazsa ve bunun yerine değişmez olarak ifade edilirse, API'nin çoğu kaybolur. Dahası, yazdığınız kod Haskell'e benzer diller için AST temsillerine daha kolay aktarılır.
Özetle, TH'nin güçlü, yarı ihmal edilmiş bir araç olduğunu düşünüyorum. Daha az nefret, daha canlı bir kütüphane eko sistemine yol açarak daha fazla dil özelliği prototipinin uygulanmasını teşvik edebilir. TH'nin, her şeyi / yapmanıza / neredeyse her şeye izin verebilecek aşırı güçlü bir araç olduğu gözlemlenmiştir. Anarşi! Benim fikrime göre, bu güç sınırlamalarının çoğunu aşmanıza ve oldukça ilkeli meta-programlama yaklaşımlarına sahip sistemler inşa etmenize izin verebilir. "Uygun" uygulamayı simüle etmek için çirkin hack'lerin kullanımına değer, çünkü bu şekilde "uygun" uygulamanın tasarımı yavaş yavaş netleşecektir.
Nirvana'nın kişisel ideal versiyonumda, dilin çoğu aslında derleyiciden, bu çeşitliliğin kütüphanelerine taşınacaktı. Özelliklerin kütüphaneler olarak uygulanması gerçeği, sadık soyutlama yeteneklerini büyük ölçüde etkilemez.
Kazan plakasına verilen tipik Haskell cevabı nedir? Soyutlama. En sevdiğimiz soyutlamalar neler? Fonksiyonlar ve tipler!
Typeclasses, o sınıfta genel olan her türlü fonksiyonda kullanılabilecek bir dizi yöntem tanımlamamıza izin verir. Ancak, bunun dışında, sınıfların ortak plakayı önlemeye yardımcı olmasının tek yolu "varsayılan tanımlar" sunmaktır. Şimdi, ilkesiz bir özellik örneği!
Minimal ciltleme setleri beyan edilemez / derleyici tarafından kontrol edilemez. Bu, karşılıklı özyineleme nedeniyle dip oluşturan yanlışlıkla tanımlara yol açabilir.
Bunun getireceği büyük kolaylık ve güce rağmen, yetim örnekler nedeniyle süper sınıf varsayılanlarını belirtemezsiniz http://lukepalmer.wordpress.com/2009/01/25/a-world-without-orphans/ Bunlar, sayısal hiyerarşi incelikle!
Yöntem varsayılanları için TH benzeri özelliklerin peşinden gitmek http://www.haskell.org/haskellwiki/GHC.Generics yolunu açtı . Bu harika şeyler olsa da, bu jenerikleri kullanan tek hata ayıklama kodum, indüklenen tipin büyüklüğü ve AST olarak karmaşık olan ADT nedeniyle neredeyse imkansızdı. https://github.com/mgsloan/th-extra/commit/d7784d95d396eb3abdb409a24360beb03731c88c
Başka bir deyişle, bu TH tarafından sağlanan özelliklerden sonra gitti, ancak dilin tüm bir alanını, inşaat dilini bir tür sistem temsiline yükseltmek zorunda kaldı. Ortak probleminiz için iyi çalıştığını görürken, karmaşık olanlar için, TH hackery'den çok daha korkunç bir sembol yığını vermeye eğilimli görünüyor.
TH size çıkış kodunun değer düzeyinde derleme zamanı hesaplamasını verirken, jenerikler sizi kodun desen eşleştirme / özyineleme bölümünü tür sistemine kaldırmaya zorlar. Bu kullanıcıyı oldukça kullanışlı bir şekilde kısıtlasa da, karmaşıklığın buna değdiğini düşünmüyorum.
TH ve lisp benzeri metaprogramlamanın reddedilmesinin, örnek bildirimleri yerine daha esnek, makro genişleme yerine yöntem varsayılanları gibi şeylere tercih edilmesine yol açtığını düşünüyorum. Öngörülemeyen sonuçlara yol açabilecek şeylerden kaçınmak disiplin akıllıca olsa da, Haskell'in yetenekli tip sisteminin diğer birçok ortamda olduğundan daha güvenilir bir meta programlamaya izin verdiğini (üretilen kodu kontrol ederek) göz ardı etmemeliyiz.