2012-04-04 15 views
5

मैं कुछ हास्केल कोड लिखने की कोशिश कर रहा हूं जिसमें कई डेटा प्रकार हैं, जिनमें से प्रत्येक में कई कार्यान्वयन हो सकते हैं। ऐसा करने के लिए, मैं प्रत्येक डेटा प्रकार को class के रूप में परिभाषित करता हूं जिनकी विधियां प्रासंगिक रचनाकार और चयनकर्ता हैं, और फिर दिए गए रचनाकारों और चयनकर्ताओं के संदर्भ में उस वर्ग के सदस्यों पर सभी परिचालनों को लागू करते हैं।प्रकार के साथ मज़ा! एकाधिक उदाहरण घोषणाओं को हल करना

उदाहरण के लिए, शायद A एक बहुपद वर्ग है जो एक SparsePoly या एक DensePoly और B के रूप में एक प्रतिनिधित्व हो सकता है (तरीकों getCoefficients और makePolynomial साथ) एक जटिल संख्या वर्ग जो हो सकता है (तरीकों getReal, getImag और makeComplex के साथ) ComplexCartesian या ComplexPolar के रूप में दर्शाया गया है।

मैं नीचे एक न्यूनतम उदाहरण reproduced गए हैं। मेरे पास दो वर्ग A और B हैं जिनमें से प्रत्येक में कार्यान्वयन है। मैं स्वचालित रूप से Num के उदाहरणों में दोनों वर्गों के सभी उदाहरण बनाना चाहता हूं (इसके लिए FlexibleInstances और UndecidableInstances प्रकार एक्सटेंशन की आवश्यकता है)। यह ठीक काम करता है जब मैं केवल A या B में से एक है, लेकिन जब मैं दोनों के साथ संकलित करने के लिए प्रयास करते हैं, मैं निम्नलिखित त्रुटि मिलती है:

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 

मुझे लगता है 'डुप्लिकेट उदाहरण घोषणाओं' संदेश है कि इसकी वजह एक डेटा प्रकार A और B दोनों का उदाहरण बनाया जा सकता है। मैं संकलक को एक वादा करने में सक्षम होना चाहता हूं कि मैं ऐसा नहीं करूँगा, या संभवतः किसी वर्ग का उपयोग करने के लिए डिफ़ॉल्ट वर्ग निर्दिष्ट करता हूं कि एक प्रकार दोनों वर्गों का एक उदाहरण है।

क्या ऐसा करने का कोई तरीका है (शायद एक और प्रकार का एक्सटेंशन?) या यह कुछ है जिसके साथ मैं फंस गया हूं?

यहाँ मेरी कोड है:

{-# 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 

संपादित करें: अपने आप को स्पष्ट करने के लिए, मैं किसी भी व्यावहारिक कोड इस तकनीक का इस्तेमाल लिखने की कोशिश कर नहीं कर रहा हूँ। मैं इसे टाइप सिस्टम और एक्सटेंशन को बेहतर ढंग से समझने में मदद करने के लिए एक अभ्यास के रूप में कर रहा हूं।

+4

संबंधित: [मैं कैसे लिखूं, "अगर टाइपक्लास ए, तो यह भी परिभाषा द्वारा बी का एक उदाहरण है।"] (Http://stackoverflow.com/a/3216937/98117)। – hammar

उत्तर

11

अपने प्रश्न का यह हिस्सा

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.

गलत है। यह वास्तव में है, क्योंकि आपको दो में,

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

कि संकलक अलग नहीं बता सकता लिखा है (@ Hammar की टिप्पणी से लिंक देखें, वर्ग संदर्भों उदाहरण घोषणाओं के बीच अंतर करने के प्रयोजन के लिए गिनती नहीं है)।

एक समाधान गवाह प्रकार जोड़ने के लिए है।

{-# 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) 

कंपाइलर आपके उदाहरण घोषणाओं के बीच अंतर करने के लिए गवाह प्रकारों का उपयोग कर सकता है।

+0

धन्यवाद, यह वह दृष्टिकोण है जिसके साथ मैं गया हूं। –

4

ऐसा करने का कोई अच्छा तरीका नहीं है; सबसे अच्छा अभ्यास

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

जो Num उदाहरणों अधिक यांत्रिक आप एक A उदाहरण है के बाद लेखन बनाता है जैसे कुछ स्थिरांक परिभाषित करने के लिए है:

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

धन्यवाद! मैं देख सकता हूं कि यह कैसे उपयोगी होगा यदि आपके पास प्रत्येक वर्ग के केवल कुछ ही कार्यान्वयन हैं। –

संबंधित मुद्दे