2012-04-04 20 views
5

Birden çok veri türü olan ve her biri birden fazla uygulama içeren bazı Haskell kodları yazmaya çalışıyorum. Bunu yapmak için, her bir veri tipini metodları ilgili kurucular ve seçiciler olan class olarak tanımlarım ve daha sonra verilen kurucular ve seçiciler açısından o sınıfın üyeleri üzerindeki tüm işlemleri uygularım.Türleri ile eğlenceli! Birden çok örnek bildirimi çözümleniyor

Örneğin, belki de A bir SparsePoly veya DensePoly ve B gibi bir gösterimi olabilir (yöntem getCoefficients ve makePolynomial ile) polinom sınıftır olabilir (yöntem getReal, getImag ve makeComplex) ile karmaşık bir sayı sınıfıdır ComplexCartesian veya ComplexPolar olarak temsil edilir.

Aşağıda minimal bir örnek ürettim. Her birinin bir uygulaması olan A ve B iki sınıfım var. Her iki sınıfın tüm örneklerini otomatik olarak Num örneğine yapmak istiyorum (bu FlexibleInstances ve UndecidableInstances tür uzantılarını gerektirir). Sadece A veya B birine sahip olduğunda bu iyi çalışır, ancak her ikisi ile derlemeye çalıştığınızda, aşağıdaki hatayı alıyorum:

Duplicate instance declarations: 
    instance [overlap ok] (A a, Num x, Show (a x), Eq (a x)) => 
         Num (a x) 
    -- Defined at test.hs:13:10-56 
    instance [overlap ok] (B b, Num x, Show (b x), Eq (b x)) => 
         Num (b x) 
    -- Defined at test.hs:27:10-56 

Ben 'yinelenen örneği beyanları' mesajı olduğunu varsayalım bir veri türü nedeniyle A ve B'un bir örneği yapılabilir. Derleyiciye bunu yapmayacağımı veya bir türün her iki sınıfın bir örneği olması durumunda kullanmak için varsayılan bir sınıf belirtebileceğine söz verebilmeyi istiyorum.

Bunu yapmanın bir yolu var mı (belki başka türde bir uzantıya mı?) Veya buna yapıştığım bir şey mi var?

İşte benim kod:

{-# LANGUAGE FlexibleInstances, UndecidableInstances, OverlappingInstances #-} 

class A a where 
    fa :: a x -> x 
    ga :: x -> a x 

data AImpl x = AImpl x deriving (Eq,Show) 

instance A AImpl where 
    fa (AImpl x) = x 
    ga x = AImpl x 

instance (A a, Num x, Show (a x), Eq (a x)) => Num (a x) where 
    a1 + a2 = ga (fa a1 + fa a2) 
    -- other implementations go here 


class B b where 
    fb :: b x -> x 
    gb :: x -> b x 

data BImpl x = BImpl x deriving (Eq,Show) 

instance B BImpl where 
    fb (BImpl x) = x 
    gb x = BImpl x 

instance (B b, Num x, Show (b x), Eq (b x)) => Num (b x) where 
    -- implementations go here 

Düzenleme: Yeterince açık hale getirmek için bu tekniği kullanarak herhangi pratik kod yazmak çalışmıyorum. Bunu, tip sistemini ve uzantıları daha iyi anlama konusunda yardımcı olmak için bir egzersiz olarak yapıyorum.

+4

İlgili: [Yazım nasıl yazılır, eğer typeclass a, sonra da bu tanım ile b'nin bir örneğidir.]] (Http://stackoverflow.com/a/3216937/98117). – hammar

cevap

11

sorunuzun bu bölümü

I suppose that the 'duplicate instance declarations' message is because a data type could be made an instance of both A and B. I want to be able to make a promise to the compiler that I won't do that, or possibly specify a default class to use in the case that a type is an instance of both classes.

yanlış. Eğer derleyici ayırt edemez iki örneği,

instance Num (a x) 
instance Num (b x) 

yazdım çünkü aslında (@ Hammar yorumuna gelen bağlantıya bakın, sınıf bağlamları örnek bildirimleri ayırt etmek amacıyla sayılmaz).

Bir çözüm, bir tanık tipi eklemektir.

{-# LANGUAGE FlexibleInstances, FlexibleContexts, UndecidableInstances, OverlappingInstances #-} 

data AWitness 

data AImpl witness x = AImpl x deriving (Eq,Show) 

instance A (AImpl AWitness) where 
    fa (AImpl x) = x 
    ga x = AImpl x 

instance (A (a AWitness), Num x, Show (a AWitness x), Eq (a AWitness x)) => Num (a AWitness x) where 
    a1 + a2 = ga (fa a1 + fa a2) 

Derleyici, örnek beyanlarınız arasında ayrım yapmak için tanık türlerini kullanabilir.

+0

Teşekkürler, gittiğim yaklaşım budur. –

4

Bunu yapmanın gerçekten iyi bir yolu yok; En iyi uygulama bir A örneği aldıktan sonra daha mekanik Num örneklerini yazma yapar

plusA, minusA :: (A a, Num x) => a x -> a x -> a x 

gibi bazı sabitleri tanımlamaktır:

instance A Foo where ... 
instance Num x => Num (Foo x) where 
    (+) = plusA 
    (-) = minusA 
+0

Teşekkürler! Her bir sınıfın sadece az sayıda uygulaması varsa, bunun nasıl yararlı olacağını görebiliyorum. –

İlgili konular