Haskell'de sahipsiz örnekler


87

Haskell uygulamamı bu -Wallseçenekle derlerken , GHC öksüz örneklerden şikayet ediyor, örneğin:

Publisher.hs:45:9:
    Warning: orphan instance: instance ToSElem Result

Tür sınıfı ToSElembenim değil, HStringTemplate tarafından tanımlandı .

Şimdi bunu nasıl düzelteceğimi biliyorum (örnek bildirimini Sonucun bildirildiği modüle taşıyın) ve GHC'nin neden öksüz örneklerden kaçınmayı tercih ettiğini biliyorum , ancak yine de yolumun daha iyi olduğuna inanıyorum. Derleyicinin rahatsız olup olmaması umurumda değil - benden ziyade.

ToSElemÖrneklerimi Publisher modülünde bildirmek istememin nedeni , diğer modüllere değil, HStringTemplate'e bağlı olan Publisher modülü olmasıdır. Endişeleri birbirinden ayırmaya ve her modülün HStringTemplate'e bağlı olmasını önlemeye çalışıyorum.

Haskell'in tip sınıflarının, örneğin Java arayüzlerine kıyasla avantajlarından birinin kapalı olmaktan çok açık olmaları ve bu nedenle örneklerin veri türü ile aynı yerde bildirilmesi gerekmediğini düşündüm. GHC'nin tavsiyesi bunu görmezden gelmek gibi görünüyor.

Yani, aradığım şey ya düşüncemin sağlam olduğunu ve bu uyarıyı görmezden gelmek / bastırmakla haklı çıkacağımı doğrulamak ya da işleri kendi yöntemimle yapmaya karşı daha ikna edici bir argüman.


Cevaplar ve yorumlardaki tartışma, bir yürütülebilir dosyada öksüz örnekleri tanımlamakla , başkalarının maruz kaldığı bir kitaplıkta yapmak arasında büyük bir fark olduğunu göstermektedir. Bu son derece popüler soru , öksüz örneklerin, onları tanımlayan bir kütüphanenin son kullanıcıları için ne kadar kafa karıştırıcı olabileceğini gösteriyor.
Christian Conkle

Yanıtlar:


94

Bunu neden yapmak istediğinizi anlıyorum, ancak ne yazık ki, Haskell sınıflarının sizin söylediğiniz şekilde "açık" görünmesi yalnızca bir yanılsama olabilir. Birçok insan, aşağıda açıklayacağım nedenlerden dolayı, bunu yapma olasılığının Haskell spesifikasyonunda bir hata olduğunu düşünüyor. Her neyse, örnek için gerçekten uygun değilse, ya sınıfın bildirildiği modülde ya da türün bildirildiği modülde bildirilmeniz gerekir, bu muhtemelen bir newtypeveya başka bir sarmalayıcı kullanmanız gerektiğine dair bir işarettir. türünün etrafında.

Yetim örneklerden kaçınılması gereken nedenler, derleyicinin rahatlığından çok daha derindir. Diğer cevaplardan da görebileceğiniz gibi, bu konu oldukça tartışmalı. Tartışmayı dengelemek için, tecrübeli Haskeller arasında çoğunluğun düşüncesi olduğunu düşündüğüm, asla, asla öksüz örnekler yazmamalı bakış açısını açıklayacağım. Kendi fikrim ortada bir yerde, sonunda açıklayacağım.

Sorun, aynı sınıf ve tür için birden fazla örnek bildirimi bulunduğunda, standart Haskell'de hangisinin kullanılacağını belirleyen bir mekanizma olmadığı gerçeğinden kaynaklanmaktadır. Aksine, program derleyici tarafından reddedilir.

Bunun en basit etkisi, modülünüzün çok uzak bir bağımlılığında başka birinin yaptığı bir değişiklik nedeniyle derlemeyi aniden durduracak mükemmel çalışan bir programa sahip olabilmenizdir.

Daha da kötüsü, uzak bir değişiklik nedeniyle çalışan bir programın çalışma zamanında çökmeye başlaması mümkündür . Belirli bir örnek bildiriminden geldiğini varsaydığınız bir yöntemi kullanıyor olabilirsiniz ve sessizce, programınızın açıklanamaz şekilde çökmeye başlamasına neden olacak kadar farklı olan farklı bir örnekle değiştirilebilir.

Bu sorunların asla başlarına gelmeyeceğinin garantisini isteyen kişiler, herhangi biri, herhangi bir yerde, belirli bir tür için belirli bir sınıfın bir örneğini ilan etmişse, başka hiçbir örnek yazılan hiçbir programda bir daha ilan edilmemelidir kuralına uymalıdır. kimse tarafından. Elbette, newtypeyeni bir örnek bildirmek için a kullanmanın geçici bir çözümü var , ancak bu her zaman en azından küçük bir rahatsızlık ve bazen de büyük bir rahatsızlıktır. Yani bu anlamda, kasıtlı olarak öksüz örnekleri yazanlar oldukça kaba davranıyorlar.

Peki bu sorunla ilgili ne yapılmalı? Anti-orphan-instance kampı, GHC uyarısının bir hata olduğunu, bir öksüz örnek bildirme girişimini reddeden bir hata olması gerektiğini söylüyor. Bu arada, öz disiplin uygulamalı ve ne pahasına olursa olsun onlardan kaçınmalıyız.

Gördüğünüz gibi, bu potansiyel sorunlar için çok endişelenmeyenler var. Aslında, sizin önerdiğiniz gibi, öksüz vakaların kaygıları ayırmak için bir araç olarak kullanılmasını teşvik ediyorlar ve bir kişinin sadece duruma göre bir sorun olmadığından emin olması gerektiğini söylüyorlar. Başkalarının öksüz durumlarından, bu tavrın fazla şövalye olduğuna ikna olacak kadar çok rahatsız oldum.

Bence doğru çözüm, Haskell'in içe aktarma mekanizmasına örneklerin içe aktarılmasını kontrol edecek bir uzantı eklemek olacaktır. Bu, sorunları tamamen çözmeyecektir, ancak programlarımızı dünyada zaten var olan öksüz vakaların zararlarına karşı korumaya biraz yardımcı olacaktır. Ve sonra, zamanla, bazı sınırlı durumlarda, belki de bir yetim vakasının o kadar da kötü olmayabileceğine ikna olabilirim. (Yetim kampındaki bazılarının benim önerime karşı çıkmasının nedeni de bu cazibedir.)

Tüm bunlardan çıkarım, en azından şimdilik, herhangi bir öksüz vakayı ilan etmekten kaçınmanızı, başka bir sebep yoksa başkalarına karşı saygılı olmanızı şiddetle tavsiye ediyorum. Bir newtype.


4
Özellikle, bu, kütüphanelerdeki büyümeyle ilgili bir sorundur. Haskell'deki 2200'den fazla kitaplık ve on binlerce bağımsız modülle, örnekleri alma riski önemli ölçüde artıyor.
Don Stewart

16
Re: "Bence doğru çözüm, Haskell'in içe aktarma mekanizmasına örneklerin içe aktarılmasını kontrol edecek bir uzantı eklemek olacaktır" Bu fikir herhangi birini ilgilendirirse, bir örnek için Scala diline bakmaya değer olabilir; tür sınıf örnekleri gibi çok kullanılabilen 'implicits' kapsamını kontrol etmek için buna çok benzer özelliklere sahiptir.
Matt

5
Yazılımım bir kitaplıktan ziyade bir uygulama, bu nedenle diğer geliştiriciler için sorun yaratma olasılığı neredeyse sıfır. Publisher modülünü uygulama ve modüllerin geri kalanını bir kitaplık olarak düşünebilirsiniz, ancak kitaplığı dağıtacak olsaydım, Publisher ve dolayısıyla öksüz örnekler olmadan olurdu. Ancak örnekleri diğer modüllere taşırsam, kütüphane HStringTemplate üzerinde gereksiz bir bağımlılıkla birlikte gelirdi. Yani bu durumda öksüzlerin iyi olduğunu düşünüyorum, ancak aynı sorunla farklı bir bağlamda karşılaşırsam tavsiyenize kulak vereceğim.
Dan Dyer

1
Bu mantıklı bir yaklaşım gibi görünüyor. O zaman dikkat etmeniz gereken tek şey, içe aktardığınız bir modülün yazarının bu örneği sonraki bir sürüme eklemesidir. Bu örnek sizinkiyle aynıysa, kendi örnek bildiriminizi silmeniz gerekir. Bu örnek sizinkinden farklıysa, türünüzün etrafına yeni bir tür sarmalayıcı koymanız gerekir - bu, kodunuzun önemli ölçüde yeniden düzenlenmesi olabilir.
Yitz

@Matt: Gerçekten, şaşırtıcı bir şekilde Scala bunu Haskell'in almadığı yerde alıyor! (tabii ki Scala, tip sınıfı makineler için birinci sınıf sözdiziminden yoksundur, ki bu daha da kötüdür ...)
Erik Kaplun

44

Devam edin ve bu uyarıyı bastırın!

İyi bir şirketsiniz. Conal bunu "TypeCompose" olarak yapar. "chp-mtl" ve "chp-transformers" yapar, "kontrol-monad-istisna-mtl" ve "kontrol-monad-istisna-monadsfd" yapar vb.

btw muhtemelen bunu zaten biliyorsunuzdur, ancak bilmeyenler ve sorunuzu bir aramada bulanlar için:

{-# OPTIONS_GHC -fno-warn-orphans #-}

Düzenle:

Yitz'in cevabında bahsettiği sorunları gerçek sorunlar olarak kabul ediyorum. Bununla birlikte, öksüz örnekleri kullanmamayı da bir sorun olarak görüyorum ve "tüm kötülüklerin en küçüğünü" seçmeye çalışıyorum, bu da öksüz örnekleri ihtiyatlı bir şekilde kullanmak anlamına geliyor.

Kısa cevabımda sadece bir ünlem işareti kullandım çünkü sorunuz zaten problemlerin farkında olduğunuzu gösteriyor. Aksi takdirde daha az hevesli olurdum :)

Biraz dikkat dağıtıcı, ancak ödün vermeden mükemmel bir dünyada mükemmel çözüm olduğuna inanıyorum:

Yitz'in bahsettiği sorunların (hangi örneğin seçildiğini bilmeden) "bütünsel" bir programlama sisteminde çözülebileceğine inanıyorum.

  • Yalnızca metin dosyalarını ilkel olarak düzenlemiyorsunuz, bunun yerine ortamdan destek alıyorsunuz (örneğin, kod tamamlama yalnızca ilgili türlerdeki şeyleri önerir, vb.)
  • "Alt düzey" dilin tür sınıfları için özel bir desteği yoktur ve bunun yerine işlev tabloları açıkça iletilir
  • Ancak, "daha yüksek seviye" programlama ortamı, kodu Haskell'in şu anda sunuluş şekline benzer şekilde görüntüler (genellikle iletilen işlev tablolarını görmezsiniz) ve açık olduklarında sizin için açık tür sınıflarını seçer (için örneğin, tüm Functor durumlarının yalnızca bir seçeneği vardır) ve birkaç örnek (zipleme listesi Uygulayıcı veya list-monad Uygulayıcı, İlk / Son / kaldırma belki Monoid) olduğunda, hangi örneğin kullanılacağını seçmenize izin verir.
  • Her durumda, örnek sizin için otomatik olarak seçildiğinde bile, ortam, kolay bir arayüzle (bir köprü veya fareyle üzerine gelme arayüzü veya başka bir şey) hangi örneğin kullanıldığını kolayca görmenize olanak tanır.

Fantezi dünyasından (veya umarız gelecekten) dönersek, şu anda: Yetim örneklerden kaçınmaya çalışmanızı ve "gerçekten ihtiyacınız" olduğunda onları kullanmaya devam etmenizi tavsiye ederim.


5
Evet, ama muhtemelen bu olayların her biri bir düzenin hatasıdır. Her ikisi için control-monad-exception-mtl ve monads-fd'deki kötü örnekler. Bu modüllerin her birinin kendi türlerini tanımlamaya veya yeni tür sarmalayıcılar sağlamaya zorlanması daha az rahatsız edici olurdu. Neredeyse her yetim vakası, gerçekleşmeyi bekleyen bir baş ağrısıdır ve başka hiçbir şey, uygun şekilde içe aktarıldığından veya alınmadığından emin olmak için sürekli dikkatinizi gerektirmez.
Edward KMETT

2
Teşekkürler. Sanırım onları bu özel durumda kullanacağım, ancak Yitz sayesinde artık hangi sorunlara neden olabileceklerini daha iyi anlıyorum.
Dan Dyer

37

Yetim vakaları bir baş belasıdır, ancak bence bazen gerekli olabilir. Genellikle bir türün bir kitaplıktan ve bir sınıfın başka bir kitaplıktan geldiği kitaplıkları birleştiririm. Elbette bu kitaplıkların yazarlarının akla gelebilecek her tür ve sınıf kombinasyonu için örnekler sağlaması beklenemez. Bu yüzden onlara sağlamam gerekiyor ve bu yüzden öksüzler.

Bir örnek sağlamanız gerektiğinde türü yeni bir türe sarmanız gerektiği fikri, teorik değeri olan bir fikirdir, ancak birçok durumda çok sıkıcıdır; yaşamak için Haskell kodu yazmayan insanlar tarafından ortaya atılan türden bir fikir. :)

Öyleyse devam edin ve yetim örnekleri sağlayın. Zararsızdırlar.
Ghc'yi öksüz örneklerle çökertebilirseniz, bu bir hatadır ve bu şekilde rapor edilmelidir. (Ghc'nin birden fazla örneği algılamamasıyla ilgili hata düzeltmesi o kadar da zor değil.)

Ancak ileride başka birinin zaten sahip olduğunuz bazı örnekleri ekleyebileceğini ve bir (derleme zamanı) hatası alabileceğinizi unutmayın.


2
(Ord k, Arbitrary k, Arbitrary v) ⇒ Arbitrary (Map k v)QuickCheck kullanırken iyi bir örnek .
Erik Kaplun

17

Bu durumda, yetim örneklerin kullanılmasının iyi olduğunu düşünüyorum. Benim için genel kural şudur: tip sınıfına "sahipseniz" veya veri türüne (veya bunun bir bileşenine "sahipseniz" - yani Belki MyData için bir örnek de iyidir, bir örnek tanımlayabilirsiniz. en azından bazen). Bu kısıtlamalar dahilinde, örneği koymaya karar verdiğiniz yer kendi işinizdir.

Bir istisna daha var - ne tip sınıfına ne de veri türüne sahipseniz, ancak bir kitaplık değil de ikili kod üretiyorsanız, bu da sorun değil.


5

(Partiye geç kaldığımı biliyorum ama bu yine de başkaları için faydalı olabilir)

Yetim örnekleri kendi modüllerinde tutabilirsiniz, o zaman herhangi biri bu modülü içe aktarırsa, özellikle onlara ihtiyaç duydukları içindir ve sorunlara neden olursa bunları içe aktarmaktan kaçınabilirler.


3

Bu doğrultuda, anti-öksüz örnek kampının WRT kitaplıklarının konumunu anlıyorum, ancak yürütülebilir hedefler için öksüz örnekler iyi olmamalı mı?


3
Başkalarına karşı kaba olma konusunda haklısın. Ancak, aynı durum gelecekte bağımlılık zincirinizin bir yerinde tanımlanırsa, kendinizi gelecekteki potansiyel sorunlara açarsınız. Yani bu durumda, riske değip değmeyeceğine karar vermek size kalmış.
Yitz

5
Yürütülebilir bir dosyada bir öksüz örnek uygulamaya ilişkin hemen hemen tüm durumlarda, bu sizin için bir boşluğu doldurmak içindir. için önceden tanımlanmış olmasını istediğiniz içindir. Dolayısıyla, örnek yukarı akışta görünürse, sonuçta ortaya çıkan derleme hatası, örnek bildiriminizi kaldırabileceğinizi söyleyen yararlı bir sinyaldir.
Ben
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.