Şablon Haskell hakkında bu kadar kötü olan nedir?


252

Şablon Haskell genellikle Haskell topluluğu tarafından talihsiz bir kolaylık olarak görülüyor. Bu konuda gözlemlediklerimi tam olarak kelimelere dökmek zor, ancak bu birkaç örneği düşünün

İnsanların Template Haskell ile oldukça düzgün şeyler yaptıkları, normal Haskell'de mümkün olmayan daha güzel sözdiziminin yanı sıra muazzam kazan plakası azaltmalarını sağlayan çeşitli blog yayınları gördüm. Öyleyse neden Şablon Haskell bu şekilde inceleniyor? Bunu istenmeyen kılan nedir? Şablon Haskell hangi koşullarda kaçınılmalıdır ve neden?


56
Hareket etme oyuna katılmıyorum; Bu soruyu sorduğum aynı ruhla soruyorum Tembel I / O hakkında bu kadar kötü olan nedir? ve aynı tarzda cevaplar görmeyi umuyorum. Bu yardımcı olursa soruyu yeniden düzenlemeye açıkım.
Dan Burton

51
@ErikPhilips Bu etiketi sık sık ziyaret edenlerin buraya ait olup olmadığına karar vermesine neden izin vermiyorsun? Görünüşe göre Haskell topluluğuyla tek etkileşim sorularınızı ortaya çıkarmak.
Gabriel Gonzalez

5
@GabrielGonzalez mevcut soru ile açıktır ve burada ne tür bir soru sormamalıyım altında SSS'yi takip etmediğini cevaplayın : çözülmesi gereken gerçek bir sorun yoktur . Sorunun kodla ilgili özel bir sorunu çözülmüş değildir ve yalnızca kavramsaldır. Ve şablon haskell önlemek gerekir sorusuna dayanarak, o da Yığın Taşması düşüyor bir Tavsiye Motoru değildir .
Erik Philips

27
@ErikPhilips Tavsiye Motoru yönü bu soru ile ilgisizdir, çünkü bunun farklı araçlar arasındaki bir karara atıfta bulunduğunu fark edeceksiniz (örn. "Hangi dili kullanmam gerektiğini söyle"). Buna karşılık, sadece Şablon Haskell ile ilgili açıklama istiyorum ve SSS "motivasyonunuz" Başkalarının bana [boş] açıklamasını istiyorum "ise, muhtemelen sorun değil." Örneğin, GOTO hala zararlı olarak mı değerlendiriliyor?
Dan Burton

29
Yeniden açmak için oylama. Daha üst düzey bir soru olması, iyi bir soru olmadığı anlamına gelmez.
György Andrasek

Yanıtlar:


171

Şablon Haskell'den kaçınmanın bir nedeni, bir bütün olarak tür güvenli değildir, bu nedenle "Haskell'in ruhunun" çoğuna karşıdır. İşte bunun bazı örnekleri:

  • Bir TH kodu parçasının ne tür Haskell AST üreteceği üzerinde, görüneceği yerin ötesinde hiçbir kontrole sahip değilsiniz; bir tür değere sahip olabilirsiniz Exp, ancak bunun bir [Char]veya bir (a -> (forall b . b -> c))veya her neyi temsil eden bir ifade olup olmadığını bilmiyorsunuz . Bir işlev bir işlevin yalnızca belirli bir türden ifadeler üretebileceğini veya yalnızca işlev bildirimlerini veya yalnızca veri yapıcı eşleştirme düzenlerini vb. Üretebileceğini ifade edebilirse TH daha güvenilir olacaktır.
  • Derlemeyen ifadeler oluşturabilirsiniz. Var olmayan bir serbest değişkene başvuran bir ifade mi oluşturdunuz foo? Zor şanslar, sadece kod üreticinizi kullanırken ve yalnızca söz konusu kodun oluşturulmasını tetikleyen koşullar altında göreceksiniz. Birim testi yapmak da çok zordur.

TH de kesinlikle tehlikelidir:

  • Derleme zamanında çalışan kod, IOfüze fırlatma veya kredi kartınızı çalma gibi keyfi olabilir . TH istismarlarını aramak için indirdiğiniz her cabal paketine bakmak istemezsiniz.
  • TH, "modül-özel" işlevlerine ve tanımlarına erişebilir ve bazı durumlarda kapsüllemeyi tamamen kesebilir.

Sonra TH işlevlerini bir kütüphane geliştiricisi olarak kullanmak için daha az eğlenceli hale getiren bazı sorunlar var:

  • TH kodu her zaman oluşturulamaz. Birisinin lensler için bir jeneratör yaptığını ve daha sık olmamasından ötürü, bu jeneratörün, diğer TH koduyla değil, sadece doğrudan "son kullanıcı" tarafından çağrılabilecek şekilde yapılandırılacağını, örneğin parametre olarak lens üretmek için tip yapıcılarının bir listesi. Kullanıcı yalnızca yazmak zorunda kalırken, bu listeyi kodda oluşturmak zordur generateLenses [''Foo, ''Bar].
  • Geliştiriciler TH kodunun oluşturulabileceğini bile bilmiyorlar . Yazabileceğini biliyor muydun forM_ [''Foo, ''Bar] generateLens? Qsadece bir monad, bu yüzden üzerinde olağan işlevlerin tümünü kullanabilirsiniz. Bazı insanlar bunu bilmez ve bu nedenle, aynı işlevlerle aynı işlevlerin birden fazla aşırı yüklü sürümünü oluştururlar ve bu işlevler belirli bir şişkinlik efektine yol açar. Ayrıca, çoğu insan jeneratörlerini Qgerek duymadıklarında bile monadda yazar, bu da yazmak gibidir bla :: IO Int; bla = return 3; bir işleve ihtiyaç duyduğundan daha fazla "çevre" veriyorsunuz ve bu işlevin istemcilerinin bu ortamı bunun bir etkisi olarak sağlaması gerekiyor.

Son olarak, TH işlevlerini son kullanıcı olarak daha az eğlenceli hale getiren bazı şeyler vardır:

  • Saydamlık. TH işlevi tipe sahip olduğunda Q Dec, bir modülün en üst düzeyinde kesinlikle herhangi bir şey üretebilir ve ne üretileceği üzerinde kesinlikle hiçbir kontrolünüz olmaz.
  • Yekparehk. Geliştirici TH'ye izin vermediği sürece TH işlevinin ne kadar üreteceğini denetleyemezsiniz; veritabanı arabirimi ve JSON serileştirme arabirimi oluşturan bir işlev bulursanız , "Hayır, yalnızca veritabanı arabirimini istiyorum, teşekkürler; Kendi JSON arabirimi yuvarlayacağım" diyemezsiniz.
  • Çalışma süresi. TH kodunun çalışması nispeten uzun sürer. Kod, her dosya derlendiğinde yeniden yorumlanır ve genellikle çalışan TH kodu tarafından yüklenmesi gereken bir ton paket gerekir. Bu derleme süresini önemli ölçüde yavaşlatır.

4
Buna şablon-haskell'in tarihsel olarak çok zayıf bir şekilde belgelendiği gerçeğini de ekleyin. (Tekrar baktım ve şimdi işler en azından marjinal olarak daha iyi gözükse de). Bu iki şey, bir Haskell acemi olduğumda TH'yi anlamak için kasıtlı olarak bana katkıda bulundu.
mightybyte

14
Şablon Haskell'in kullanımının aniden bildirimlerin sıralaması anlamına geldiğini unutmayın! TH, Haskell'in pürüzsüz cilası göz önüne alındığında (1.4, '98, 2010 veya hatta Glasgow olsun) umut kadar sıkı entegre değildir.
Thomas M. DuBuisson

13
Haskell hakkında çok fazla zorluk çekmeden akla gelebilirsiniz, Template Haskell hakkında böyle bir garanti yoktur.
Ağustos

15
Ve Oleg'in TH'ye güvenli bir alternatif vaadine ne oldu? Onun "Sonunda Tagless, Kısmen Değerlendirildi" makalesine ve burada daha fazla notuna dayanan çalışmalarına atıfta bulunuyorum . Bunu duyurduklarında çok umut vericiydi ve daha sonra bunun hakkında başka bir kelime duymadım.
Gabriel Gonzalez


53

Bu sadece benim fikrim.

  • Kullanmak çirkin. $(fooBar ''Asdf)sadece hoş görünmüyor. Yüzeysel, tabii, ama katkıda bulunur.

  • 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 önemsediğiniz ama hala sevk gerekmez durumlarda her zaman bir sürü var, büyük ve hantal olduğunu ve Umurunda vakalar hakkında birden çok benzer ama aynı olmayan formlarda mevcut olma eğilimindedir (veri vs newtype, kayıt tarzı ile normal kurucuları vb.). Yazmak sıkıcı ve tekrarlayıcı ve mekanik olmayacak kadar karmaşık. Reform önerisi adresleri bu bazıları (tırnak daha yaygın uygulanabilir yapma).

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

  • Disiplinsiz. 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. Yazım sınıfları için, genellikle örneklerin uyması gereken 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 jenerik özelliklerini kullanırsanız, "toplam türleri için böyle çalışır, ürün türleri için böyle çalışır" diyebilirsiniz. Şablon Haskell, sadece 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. *

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

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

  • İş için doğru aracı ve yeterli olacak en küçük aracı kullanmanız gerektiğine dair genel bir prensip vardır ve bu benzetmede Şablon Haskell böyle bir şeydir . Bunu yapmanın bir yolu Şablon Haskell değilse, genellikle tercih edilir.

Template Haskell'in avantajı, onunla başka bir şekilde yapamayacağınız şeyleri yapabilmeniz ve büyük bir şey olmasıdır. Çoğu zaman TH'nin kullanıldığı şeyler, yalnızca doğrudan derleyici özellikleri olarak uygulandıklarında yapılabilir. TH, her ikisine de sahip olmak son derece yararlıdır çünkü bu şeyleri yapmanıza izin verir ve potansiyel derleyici uzantılarını çok daha hafif ve tekrar kullanılabilir bir şekilde prototiplemenize izin verir (örneğin, çeşitli lens paketlerine bakın).

Template Haskell'e karşı neden olumsuz duygular olduğunu düşündüğümü özetlemek gerekirse: Çok fazla sorunu çözüyor, ancak çözdüğü herhangi bir sorun için, bu sorunu çözmek için daha uygun, daha zarif, disiplinli bir çözüm olmalı, sorunu otomatik olarak kazan plakasını üreterek değil, kazan plakasına sahip olma ihtiyacını ortadan kaldırarak çözer .

* Sıklıkla CPPçözebileceği sorunlar için daha iyi bir güç / ağırlık oranına sahip olduğunu hissediyorum .

EDIT 23-04-14: Yukarıda sık sık uğraşmaya çalıştığım ve sadece son zamanlarda tam olarak elde ettiğim şey, soyutlama ve tekilleştirme arasında önemli bir ayrım olması. Düzgün soyutlama genellikle tek bir yan etki olarak veri tekilleştirme ile sonuçlanır ve çoğaltma genellikle yetersiz soyutlamanın açık bir göstergesidir, ancak bu yüzden değerli değildir. Doğru soyutlama, kodu doğru, anlaşılabilir ve sürdürülebilir kılan şeydir. Tekilleştirme, yalnızca daha kısa yapar. Şablon Haskell, genel olarak makrolar gibi, tekilleştirme için bir araçtır.


"CPP çözebileceği sorunlar için daha iyi bir güç / ağırlık oranına sahip". Aslında. Ve C99'da ne istersen onu çözebilir. Bunları düşünün: COS , Kaos . İnsanların neden AST jenerasyonunun daha iyi olduğunu düşündüklerini de anlamıyorum. Bu sadece daha belirsiz ve diğer dil özelliklerine daha az dik
Britton Kerin

31

Dflemstr'in getirdiği birkaç noktayı ele almak istiyorum.

TH'yi bu kadar endişe verici olarak kontrol edemediğiniz gerçeğini bulamıyorum. Neden? Çünkü bir hata olsa bile yine de derleme zamanı olacaktır. Bunun argümanımı güçlendirip güçlendirmediğinden emin değilim, ama bu C ++ şablonlarını kullanırken aldığınız hatalara benzer. Bu hataların, oluşturulan kodun oldukça basılı bir sürümünü alacağınız için, C ++ hatalarından daha anlaşılabilir olduğunu düşünüyorum.

Bir TH ifadesi / yarı-quoter, zor köşeleri gizleyebilecek kadar gelişmiş bir şey yaparsa, belki de kötü tavsiye edilir?

Son zamanlarda üzerinde çalıştığım yarı-alıntılarla bu kuralı biraz kırıyorum (haskell-src-exts / meta kullanarak) - https://github.com/mgsloan/quasi-extras/tree/master/examples . Bunun, genelleştirilmiş liste kavrayışlarına ekleme yapamamak gibi bazı hataları getirdiğini biliyorum. Ancak, http://hackage.haskell.org/trac/ghc/blog/Template%20Haskell%20Proposal'daki bazı fikirlerin derleyiciye ulaşması için iyi bir şans olduğunu düşünüyorum . O zamana kadar, Haskell'i TH ağaçlarına ayrıştırma kütüphaneleri neredeyse mükemmel bir yaklaşımdır.

Derleme hızı / bağımlılıkları ile ilgili olarak, üretilen kodu satır içine almak için "sıfırıncı" paketi kullanabiliriz. Bu, en azından belirli bir kütüphanenin kullanıcıları için iyidir, ancak kütüphaneyi düzenleme konusunda daha iyisini yapamayız. TH bağımlılıkları üretilen ikili dosyaları şişirebilir mi? Derlenmiş kod tarafından başvurulan her şeyi dışarıda olduğunu düşündüm.

Haskell modülünün derleme adımlarının aşamalandırma kısıtlaması / bölünmesi berbattır.

RE Opaklık: Bu, çağırdığınız herhangi bir kütüphane işlevi için aynıdır. Data.List.groupBy'nin ne yapacağını denetleyemezsiniz. Sürüm numaralarının size uyumluluk hakkında bir şeyler söylediği makul bir "garanti" / kuralınız vardır. Ne zaman farklı bir değişim meselesi.

Bu, sıfırıncı kullanımın işe yaradığı yerdir - oluşturulan dosyaları zaten versiyonluyorsunuz - böylece üretilen kodun formunun ne zaman değiştiğini her zaman bileceksiniz. Farklı kodlara bakıldığında, farklı kodlara bakmak biraz gnarly olabilir, bu yüzden daha iyi bir geliştirici arayüzünün kullanışlı olacağı bir yer.

RE Monolitizmi: Bir TH ifadesinin sonuçlarını kesinlikle kendi derleme zamanı kodunuzu kullanarak işleyebilirsiniz. Üst düzey bildirim türüne / adına filtre uygulamak çok fazla kod olmaz. Heck, bunu genel olarak yapan bir fonksiyon yazmayı hayal edebilirsiniz. Yarı quisquoter'ları değiştirmek / monolitize etmek için, "QuasiQuoter" üzerinde eşleşme paterni kullanabilir ve kullanılan dönüşümleri çıkarabilir veya eskileri açısından yeni bir tane yapabilirsiniz.


1
Opaklık / Monolitizm Hakkında : Elbette [Dec]istemediğiniz şeyleri gözden geçirebilir ve kaldırabilirsiniz, ancak diyelim ki fonksiyon JSON arabirimini oluştururken harici bir tanım dosyasını okur. Bunu kullanmamanız Dec, jeneratörü tanım dosyasını aramayı durdurmaz ve derlemeyi başarısız hale getirir. Bu nedenle, Qmonadın yeni adlar (ve benzeri şeyler) oluşturmanıza izin veren ancak izin vermeyecek daha kısıtlayıcı bir sürümüne sahip olmak güzel olurdu IO, böylece söylediğin gibi, sonuçlarını filtreleyebilir / diğer kompozisyonları yapabilir .
dflemstr

1
Kabul ediyorum, Q / teklifin IO olmayan bir sürümü olmalı! Bu ayrıca çözüme yardımcı olur - stackoverflow.com/questions/7107308/… . Derleme zamanı kodunun güvenli olmayan bir şey yapmadığından emin olduktan sonra, ortaya çıkan kodda güvenlik denetleyicisini çalıştırabildiğiniz ve özel öğelere başvurmadığından emin olabilirsiniz.
mgsloan

1
Hiç karşıt tezgâhınızı yazdınız mı? Hala söz verdiğin bağlantıyı bekliyorum. Bu cevabın eski içeriğini arayanlar için: stackoverflow.com/revisions/10859441/1 ve silinen içeriği görüntüleyenler için: stackoverflow.com/revisions/10913718/6
Dan Burton

1
hackage'de olduğundan daha güncel bir sıfırıncı sürüm var mı? Bu (ve darcs repo) en son 2009'da güncellendi. Her iki kopya da mevcut bir ghc (7.6) ile oluşturulmaz.
aavogt

1
@aavogt tgeeky bunu düzeltmek için çalışıyordu, ama bitirdiğini sanmıyorum: github.com/technogeeky/zeroth Eğer kimse bunu yapmazsa / bunun için bir şey yaparsa, sonunda bununla ilgileneceğim .
mgsloan

16

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.


4
Bu cevap tek başına iyi durumda değil: seninkini düzgün okuyabilmek için gitmem ve bulmam gereken başka bir cevaba bir sürü referans yapıyorsun.
Ben Millwood

Bu doğru. Buna rağmen ne konuşulduğunu netleştirmeye çalıştım. Belki de satır içi illisuis'in puanlarını düzenleyeceğim.
mgsloan

Belki de "ilkesiz" kelimesinin kullanmam gerekenden daha güçlü bir kelime olduğunu söylemeliyim, niyet etmediğim bazı çağrışımlarla - kesinlikle vicdansız değil! Bu mermi noktası en çok sıkıntı çektiğim şeydi, çünkü kafamda bu duygu ya da biçimlenmemiş bir fikrim var ve onu kelimelere dökmekte sorun yaşıyorum ve "ilkesiz", yakınlarda bir yerde olan kavradığım kelimelerden biriydi. "Disiplinli" muhtemelen daha yakındır. Net ve özlü bir formülasyonu olmayan bazı örnekler için gittim. Birisi bana muhtemelen ne demek istediğimi daha açık bir şekilde açıklayabilirse, bunu takdir ediyorum!
glaebhoerl

(Ayrıca sanırım evreleme kısıtlamasını ve çirkin yazmak için alıntıları değiştirmiş olabilirsiniz.)
glaebhoerl

1
Doh! Bunu işaret ettiğiniz için teşekkürler! Sabit. Evet, disiplinsiz bir şekilde ifade etmenin daha iyi bir yolu olabilir. Buradaki ayrımın gerçekten "yerleşik" ve dolayısıyla "iyi anlaşılmış" soyutlama mekanizmaları ile "ad-hoc" veya kullanıcı tanımlı soyutlama mekanizmaları arasında olduğunu düşünüyorum. Ne demek istediğimi düşününce, daktilo gönderimine oldukça benzer bir şey uygulayan bir TH kütüphanesini tanımlayabileceğinizi varsayalım (derleme zamanında olsa da)
mgsloan

8

Template Haskell ile ilgili oldukça pragmatik bir sorun, yalnızca GHC'nin bayt kodu yorumlayıcısı mevcut olduğunda işe yaramasıdır, bu tüm mimarilerde geçerli değildir. Dolayısıyla, programınız Şablon Haskell kullanıyorsa veya onu kullanan kitaplıklara dayanıyorsa, ARM, MIPS, S390 veya PowerPC CPU'lu makinelerde çalışmaz.

Bu uygulamada önemlidir: git-annex , Haskell'de yazılmış ve depolama konusunda endişe duyan makinelerde çalışmak için mantıklı bir araçtır, bu tür makineler genellikle i386-CPU'lara sahip değildir. Şahsen, bir NSLU 2'de (32 MB RAM, 266MHz CPU; gitk -ekini çalıştırıyorum ; Haskell'in bu tür donanımlarda iyi çalıştığını biliyor muydunuz?) Template Haskell'i kullanırsa, bu mümkün değildir.

(ARM'deki GHC ile ilgili durum bu günlerde çok iyileşiyor ve bence 7.4.2 bile çalışıyor, ancak nokta hala duruyor).


1
“Template Haskell, ekleme ifadelerini çalıştırmak için GHC'nin yerleşik bayt kodu derleyicisine ve yorumlayıcısına güveniyor.” - haskell.org/ghc/docs/7.6.2/html/users_guide/…
Joachim Breitner

2
ah, ben - hayır, TH bayt kodu yorumlayıcısı olmadan çalışmaz, ancak bu ghci'den (bununla ilgili olsa da) farklıdır. Ghci'nin bayt kodu yorumlayıcısına bağlı olduğu göz önüne alındığında, ghci'nin kullanılabilirliği ile bayt kodu yorumlayıcısının kullanılabilirliği arasında mükemmel bir ilişki varsa şaşırmazdım, ancak sorun, ghci eksikliğine değil bayt kodu yorumlayıcısının olmamasıdır. özellikle.
muhmuhten

1
(tesadüfen, ghci TH'nin interaktif kullanımı için eğlenceli bir şekilde zayıf bir desteğe sahiptir ghci -XTemplateHaskell <<< '$(do Language.Haskell.TH.runIO $ (System.Random.randomIO :: IO Int) >>= print; [| 1 |] )')
muhmuhten

Tamam, ghci ile GHC'nin kodu ghci ikili dosyasında veya TH ekinden etkileşimli olarak gelip gelmediğinden bağımsız olarak kodu derleme (derleme yerine) yeteneğinden bahsediyordum.
Joachim Breitner

6

TH neden kötü? Benim için şöyle geliyor:

Kendini otomatik olarak oluşturmak için TH'yi kullanmaya çalışırken bulduğunuz kadar tekrarlayan kod üretmeniz gerekiyorsa, yanlış yapıyorsunuz!

Bunu düşün. Haskell'in cazibesinin yarısı, yüksek seviyeli tasarımının, diğer dillerde yazmanız gereken büyük miktarda işe yaramaz demirbaş kodundan kaçınmanıza izin vermesidir. Eğer varsa gerek kod oluşturma zamanı derleme, temelde dil veya uygulama tasarımı ya sizi başarısız olduğunu söylüyorsun. Ve biz programcılar başarısız olmaktan hoşlanmıyoruz.

Bazen, elbette, gerekli. Ancak bazen tasarımlarınızla biraz daha akıllıca davranarak TH'ye ihtiyaç duyabilirsiniz.

(Diğer bir şey, TH'nin oldukça düşük seviyeli olması. Büyük bir üst düzey tasarım yok; birçok GHC'nin iç uygulama ayrıntıları ortaya çıkıyor. Bu da API'yi değişime yatkın hale getiriyor ...)


Özellikle QuasiQuotes'u düşünürsek, dilinizin veya uygulamanızın başarısız olduğu anlamına gelmez. Sözdizimi söz konusu olduğunda her zaman ödünleşmeler vardır. Bazı sözdizimi belirli bir etki alanını daha iyi tanımlar, bu nedenle bazen başka bir sözdizimine geçmek isteyebilirsiniz. QuasiQuotes, sözdizimleri arasında zarif bir şekilde geçiş yapmanızı sağlar. Bu çok güçlüdür ve Yesod ve diğer uygulamalar tarafından kullanılır. HTML gibi hissedilen sözdizimini kullanarak HTML oluşturma kodu yazabilmek inanılmaz bir özelliktir.
CoolCodeBro

@CoolCodeBro Evet, yarı alıntı oldukça güzel ve sanırım TH'ye biraz dik. (Açıkçası TH'nin üzerine uygulanmaktadır.) TH'yi sınıf örnekleri oluşturmak veya birden fazla sanatın işlevlerini veya bunun gibi bir şeyi oluşturmak için kullanmayı düşünüyordum.
Matematiksel
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.