Haskell'in tip denetleyicisi makul. Sorun şu ki, kullandığınız bir kütüphanenin yazarları ... daha az makul bir şey yapmış.
Kısa cevap: Evet, 10 :: (Float, Float)bir örnek varsa tamamen geçerlidir Num (Float, Float). Derleyicinin veya dilin bakış açısından bunda "çok yanlış" hiçbir şey yok. Sayısal değişmezlerin ne işe yaradığına dair sezgilerimizle uyuşmuyor. Yaptığınız hatayı yakalayan tip sistemine alışkın olduğunuz için, haklı olarak şaşırdınız ve hayal kırıklığına uğradınız!
Numörnekler ve fromIntegersorun
Derleyicinin kabul etmesine şaşırıyorsunuz 10 :: Coord, yani 10 :: (Float, Float). Gibi sayısal değişmez değerlerin 10"sayısal" türlere sahip olduğu sonucuna varmak mantıklıdır . Kutudan, sayısal hazır olarak yorumlanabilir Int, Integer, Float, veya Double. Başka bir bağlamı olmayan bir dizi sayı, bu dört türün sayı olma biçiminde bir sayı gibi görünmüyor. Hakkında konuşmuyoruz Complex.
Neyse ki ya da maalesef Haskell çok esnek bir dildir. Standart, bir tamsayı değişmezinin türüne sahip 10olarak yorumlanacağını belirtir . Dolayısıyla , kendisi için bir örneği yazılmış herhangi bir tür olarak çıkarılabilir . Bunu başka bir cevapta biraz daha detaylı anlatıyorumfromInteger 10Num a => a10Num .
Dolayısıyla, sorunuzu gönderdiğinizde deneyimli bir Haskeller 10 :: (Float, Float), kabul edilmek için Num a => Num (a, a)veya gibi bir durum olması gerektiğini hemen fark etti Num (Float, Float). İçinde böyle bir örnek yok Prelude, bu yüzden başka bir yerde tanımlanmış olmalı. Kullanarak :i Num, nereden geldiğini çabucak fark ettiniz:gloss paket.
Eş anlamlıları ve öksüz örnekleri yazın
Ama bir dakika bekleyin. glossBu örnekte herhangi bir tür kullanmıyorsunuz ; olay glosssizi neden etkiledi? Cevap iki adımda geliyor.
İlk olarak, anahtar kelimeyle tanıtılan bir tür eşanlamlısı typeyeni bir tür oluşturmaz . Modülünüzde yazmak Coordbasitçe kısaltmadır (Float, Float). Aynı şekilde Graphics.Gloss.Data.Point, Pointanlamı (Float, Float). Başka bir deyişle, sizin Coordve gloss'ler Pointtam anlamıyla eşdeğerdir.
Dolayısıyla, geliştiriciler glossyazmayı seçtiklerinde instance Num Point where ..., sizin Coordyazınızı da bir örneği yaptılar Num. Bu instance Num (Float, Float) where ...veya ile eşdeğerdir instance Num Coord where ....
(Varsayılan olarak Haskell, tür eş anlamlılarının sınıf örnekleri olmasına izin vermez. glossYazarlar, bir çift dil uzantısı etkinleştirmelidir TypeSynonymInstancesveFlexibleInstances örneği yazmak için.)
İkincisi, bu şaşırtıcı çünkü bu bir öksüz örnek , yani instance C Aher ikisinin Cve Adiğer modüllerde tanımlandığı bir örnek bildirimi . Burada her parçası dahil çünkü yani özellikle ayırma sahasında Num, (,)ve Floatgelir, Preludeve her yerde kapsamında olması muhtemeldir.
Beklentiniz, içinde Numtanımlanandır Prelude, tuples ve Floattanımlanır Prelude, bu nedenle bu üç şeyin nasıl çalıştığına dair her şey,Prelude . Tamamen farklı bir modülü içe aktarmak neden herhangi bir şeyi değiştirsin? İdeal olarak olmaz, ancak öksüz örnekler bu sezgiyi kırar.
(GHC'nin öksüz örnekler hakkında uyardığını unutmayın - yazarları glossözellikle bu uyarıyı geçersiz kılar. Bu, bir kırmızı bayrak kaldırmalı ve en azından belgelerde bir uyarı vermelidir.)
Sınıf örnekleri geneldir ve gizlenemez
Ayrıca, sınıf örnekleri olan küresel : geçişli ithal herhangi modülünde tanımlanmış herhangi bir oluşum için örnek çözünürlüğünü yaparken typechecker için bağlam ve mevcut olacaktır modülü. Bu, genel muhakemeyi uygun hale getirir, çünkü (genellikle) bir sınıf işlevinin (+)belirli bir tür için her zaman aynı olacağını varsayabiliriz . Ancak, yerel kararların küresel etkileri olduğu anlamına da gelir; Bir sınıf örneğini tanımlamak, aşağı akış kodunun bağlamını geri alınamaz bir şekilde değiştirir, onu modül sınırlarının arkasına gizlemenin veya maskelemenin bir yolu yoktur.
Sen örneklerini içe kaçınmak önemlidir listelerini kullanamazsınız . Benzer şekilde, tanımladığınız modüllerden örnekleri dışa aktarmayı engelleyemezsiniz.
Bu, Haskell dil tasarımının sorunlu ve çok tartışılan bir alanıdır. Bu reddit başlığında ilgili konularla ilgili büyüleyici bir tartışma var . Örneğin, Edward Kmett'in örnekler için görünürlük kontrolüne izin verme konusundaki yorumuna bakın: "Temelde yazdığım kodun neredeyse tamamının doğruluğunu atıyorsunuz."
(Aynı Bu arada, bu yanıt gösterdi , sen yapabilirsiniz yetim örneklerini kullanarak bazı açısından küresel örnekli varsayımını kırmak!)
Ne yapmalı - kütüphane uygulayıcıları için
Uygulamadan önce iki kez düşünün Num. Sen geçici bir çözüm olamaz fromIntegersorunu-hayır, tanımlama fromInteger = error "not implemented"yok değil daha iyi yapmak. Tam sayı gerçek değerlerinin yanlışlıkla örneklediğiniz türe sahip olduğu sonucuna varılırsa, kullanıcılarınızın kafası karışacak veya şaşıracak mı - ya da daha kötüsü, asla fark etmeyecek mi? Sağlamak (*)ve (+)bu kritik mi - özellikle de hacklemeniz gerekiyorsa?
Conal Elliott adlı gibi bir kütüphanede tanımlı alternatif aritmetik operatörler kullanmayı düşünün vector-space(tür türleri için *veya Edward Kmett en) linear(tür tipleri için * -> *). Kendi kendime yapma eğilimim bu.
Kullanın -Wall. Yetim örnekleri uygulamayın ve öksüz örnek uyarısını devre dışı bırakmayın.
Alternatif olarak, lineariyi huylu kitaplıkların liderliğini ve diğer pek çok kütüphaneyi takip edin ve .OrphanInstancesveya ile biten ayrı bir modülde öksüz örnekler sağlayın .Instances. Ve bu modülü başka herhangi bir modülden içe aktarmayın . Daha sonra kullanıcılar isterlerse yetimleri açıkça ithal edebilirler.
Kendinizi öksüzleri tanımlarken bulursanız, mümkün ve uygunsa, üst düzey bakıcılardan bunları uygulamalarını istemeyi düşünün. Yetim örneğini Show a => Show (Identity a)onlar ekleyene kadar sık sık yazardım transformers. Hatta bununla ilgili bir hata raporu hazırlamış olabilirim; Ben hatırlamıyorum
Ne yapmalı - kütüphane tüketicileri için
Çok seçeneğiniz yok. Kütüphane sorumlularına - kibarca ve yapıcı bir şekilde! - ulaşın. Onları bu soruya yönlendirin. Sorunlu yetimi yazmak için özel bir nedenleri olabilir veya farkına varmamış olabilirler.
Daha genel olarak: Bu olasılığın farkında olun. Bu, Haskell'in gerçek küresel etkilerin olduğu birkaç alandan biridir; İçe aktardığınız her modülün ve bu modüllerin içe aktardığı her modülün artık örnekleri uygulamadığını kontrol etmeniz gerekir . Tip açıklamaları bazen sizi sorunlara karşı uyarabilir ve tabii ki :ikontrol etmek için GHCi'de kullanabilirsiniz.
Yeterince önemliyse eşanlamlı kelimeler newtypeyerine kendi sözcüklerinizi tanımlayın type. Onlara kimsenin bulaşmayacağından oldukça emin olabilirsiniz.
Açık kaynaklı bir kitaplıktan sık sık sorun yaşıyorsanız, elbette kitaplığın kendi sürümünüzü oluşturabilirsiniz, ancak bakım çabucak baş ağrısına dönüşebilir.