2015-03-13 7 views
7

डालने वाला मैं आवेदन की तरह एक सीआरयूडी लिख रहा हूं और प्राथमिक कुंजी (प्राथमिक कुंजी के विभिन्न प्रकार हो सकते हैं) द्वारा बहुत सारे लुकअप हैं। इसलिए मैं typeclass निम्नलिखित परिभाषित:ईक टाइपक्लास

{-# LANGUAGE MultiParamTypeClasses #-} 

class Eq b => HasPK a b where 
    getPK :: a -> b 

अब मैं लिख सकते हैं:

import Data.Maybe 

lookupPK :: HasPK a b => b -> [a] -> Maybe a 
lookupPK s = listToMaybe . filter ((== s) . getPK) 

अब, जब मैं पी के साथ दो बातें की तुलना करना चाहते हैं, मैं सिर्फ अपने पी के तुलना करना चाहते हैं। तो, मैं इस परिभाषित करने के लिए कोशिश कर रहा हूँ:

{-# LANGUAGE FlexibleInstances #-} 
{-# LANGUAGE UndecidableInstances #-} 

instance (HasPK a b) => Eq a where 
    (==) = (==) `on` getPK 

लेकिन अब यह मुझे देता है:

src/Utils.hs:61:10: Could not deduce (HasPK a b0) … 
     arising from the ambiguity check for an instance declaration 
    from the context (HasPK a b) 
     bound by an instance declaration: HasPK a b => Eq a 
     at /home/utdemir/workspace/.../Utils.hs:61:10-28 
    The type variable ‘b0’ is ambiguous 
    In the ambiguity check for: forall a b. HasPK a b => Eq a 
    To defer the ambiguity check to use sites, enable AllowAmbiguousTypes 
    In the instance declaration for ‘Eq a’ 
Compilation failed. 

किसी को भी व्याख्या कर सकते हैं इस त्रुटि मेरे लिए? क्या मैं सही रास्ते पर हूं, या क्या मैं चाहता हूं कि हासिल करने का एक सुरक्षित तरीका है?

उत्तर

9

आप की जरूरत है यहाँ एक कार्यात्मक निर्भरता:

class Eq b => HasPK a b | a -> b where 
    getPK :: a -> b 

का उपयोग करें और के लिए जो कुछ भी विस्तार GHC अंक सक्षम करें। के बाद से ऊपर डबल उदाहरण जोड़ा जा सकता है

मुद्दा,

instance HasPK MyType Int where ... 
instance HasPK MyType String where ... 

में के रूप में एक ही प्रकार के लिए कई पीकेएस होने की संभावना में निहित है बाद में, (==) `on` getPK की तरह कोड संभावित अस्पष्ट है। दरअसल, जब ऊपर दिए गए उदाहरण जोड़े जाते हैं, तो यह निर्दिष्ट नहीं करता है कि Int पीके या String एक का उपयोग करना है या नहीं।


हालांकि, जैसे

instance (HasPK a b) => Eq a where 
    ... 

एक उदाहरण है, क्योंकि यह किसी भी अन्य Eq उदाहरण के साथ ओवरलैप हो, समस्याएं पैदा होने की संभावना है। यहां सावधान रहें। मैं बजाय

equalPK :: HasPK a b => a -> a -> Bool 
equalPK = (==) `on` getPK 

लिखना और फिर इस रूप में सभी प्रासंगिक प्रकार के स्पष्ट उदाहरण हैं प्रदान करेगा:

instance Eq MyType1 where (==) = equalPK 
instance Eq MyType2 where (==) = equalPK 
... 

एक और विकल्प के रूप में, आप

class HasPK a where 
    type PK a 
    getPK :: a -> PK a 

equalPK :: Eq (PK a) => a -> a -> Bool 
equalPK = (==) `on` getPK 

instance Eq MyType1 where (==) = equalPK 
... 
के रूप में एक प्रकार परिवार में इस तरह उपयोग कर सकते हैं
+0

हां, उस उदाहरण को परिभाषित करने के बाद, मुझे 'src/Utils.hs: 52: 20: '/ =' के उपयोग से उत्पन्न ईक इंटीजर के लिए ओवरलैपिंग उदाहरण ... मिलान करने वाले उदाहरण: उदाहरण ईक इंटीजर - 'पूर्णांक-जीएमपी में परिभाषित: जीएचसी.इंटर। टाइप' उदाहरण (ईक बी, हैस्पीके ए बी) => ईक ए'। लेकिन मुझे उम्मीद नहीं थी कि 'इंटीगर' के बाद से त्रुटि 'हैस्पीक इंटीजर बी' उदाहरण नहीं मिली है। अगर मैं ईक और हैसपीके दोनों को परिभाषित करता हूं तो मैं त्रुटि को समझूंगा, लेकिन चूंकि 'हैस्पीके इंटीजर' नहीं है, तो क्या इसे सीधे सामान्य 'ईक' उदाहरण का उपयोग नहीं करना चाहिए? – utdemir

+2

@utdemir समस्या यह है: एक 'उदाहरण सी ए => ईक ए' प्रत्येक प्रकार पर लागू होता है, यहां तक ​​कि जिनके लिए 'सी ए' झूठा है (!)। हास्केल इस उदाहरण का उपयोग करने के लिए प्रतिबद्ध होगा, और जब 'सी' यह झूठा होगा, तो यह पीछे हटने और अन्य उदाहरणों की तलाश में त्रुटि होगी। ऐसा इसलिए किया जाता है क्योंकि समस्या को पीछे हटाना बहुत मुश्किल हो जाता है, और हास्केल डिजाइनर संकलन समय से चिंतित थे।जीएचसी में 'ओवरलैपिंग इंस्टेंस' एक्सटेंशन है जो इस प्रतिबंध को आराम देता है, लेकिन मैं इसकी अनुशंसा नहीं करता। ओवरलैपिंग उदाहरणों से बचा जाना सर्वोत्तम है, आईएमएचओ। – chi

+0

धन्यवाद, मैं परिवार के समाधान के साथ चला गया क्योंकि ऐसा लगता है कि कम से कम विवादास्पद विस्तार वह है। – utdemir

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