2012-07-29 6 views
9

निम्नलिखित कोड उदाहरण पर विचार करें:हास्केल उप-टाइपक्लास के लिए अनिश्चितता की आवश्यकता है?

{-# LANGUAGE FlexibleInstances #-} 
{-# LANGUAGE UndecidableInstances #-} -- Is there a way to avoid this? 

-- A generic class with a generic function. 
class Foo a where 
    foo :: a -> a 

-- A specific class with specific functions. 
class Bar a where 
    bar :: a -> a 
    baz :: a -> a 

-- Given the specific class functions, we can implement the generic class function. 
instance Bar a => Foo a where 
    foo = bar . baz 

-- So if a type belongs to the specific class... 
instance Bar String where 
    bar = id 
    baz = id 

-- We can invoke the generic function on it. 
main :: IO() 
main = 
    putStrLn (foo "bar") 

(मेरे वास्तविक कोड तरह से और अधिक व्यापक है, यह एक न्यूनतम उबला हुआ डाउन मामले पैटर्न प्रदर्शित करने के लिए है।)

यह मुझे क्यों करने के लिए स्पष्ट नहीं है UndecidableInstances यहां आवश्यक हैं - प्रकार पैरामीटर aBar a => Foo a के दोनों किनारों में एक बार प्रकट होता है, इसलिए मुझे उम्मीद है कि चीजें "बस काम करें"। मैं स्पष्ट रूप से यहाँ कुछ याद कर रहा हूँ। लेकिन किसी भी दर पर, UndecidableInstances का उपयोग किए बिना ऐसा करने का कोई तरीका है?

+2

'उदाहरण बार एक => फू की घोषणा A' आप हर' A' के लिए एक उदाहरण पेश किया है से - संदर्भ 'बार A' उदाहरण चयन में नहीं किया जाता है - हालांकि अगर आप अतिव्यापी इंस्टेंस GHC सबसे मिलेगा प्रति मॉड्यूल आधार पर विशिष्ट एक। –

+0

यह है ... काउंटर अंतर्ज्ञानी :-) क्या इसके चारों ओर कोई रास्ता है? मुझे लगता है कि "सबसे विशिष्ट उदाहरण ढूंढना" है जो 'UndecidableInstances' करने का प्रयास करेगा, लेकिन मेरे कोड में यह बुरी तरह विफल हो जाता है। –

+0

संबंधित: http://stackoverflow.com/questions/3213490 – sdcvvc

उत्तर

8

कुछ दृष्टिकोण हैं जो आप ले सकते हैं; मुझे नहीं लगता कि आपने यह निर्धारित करने के लिए पर्याप्त संदर्भ प्रदान किया है कि कौन सा सबसे उपयुक्त होगा। यदि आप जीएचसी-7.4 का उपयोग कर रहे हैं, तो आप DefaultSignatures एक्सटेंशन को आजमा सकते हैं।

{-# LANGUAGE FlexibleInstances #-} 
{-# LANGUAGE DefaultSignatures #-} 

-- A generic class with a generic function. 
class Foo a where 
    foo :: a -> a 
    default foo :: Bar a => a -> a 
    foo = bar . baz 

-- A specific class with specific functions. 
class Bar a where 
    bar :: a -> a 
    baz :: a -> a 

instance Bar String where 
    bar = id 
    baz = id 

instance Foo String 

main :: IO() 
main = 
    putStrLn (foo "bar") 

तुम अब भी घोषित करने के लिए है कि एक प्रकार Foo का एक उदाहरण है की जरूरत है, लेकिन आप विधि घोषणा को दोहराने के लिए क्योंकि डिफ़ॉल्ट कार्यान्वयन का उपयोग किया जाएगा जरूरत नहीं है।

एक और काफी हल्का दृष्टिकोण एक नया प्रकार का उपयोग करना है। यदि आपके पास ऐसे फ़ंक्शन हैं जिन्हें Foo उदाहरण की आवश्यकता है, तो आप नए टाइप में Bar इंस्टेंस को लपेट सकते हैं।

newtype FooBar a = FooBar { unFooBar :: a } 

instance Bar a => Foo (FooBar a) where 
    foo = FooBar . bar . baz . unFooBar 

-- imported from a library or something... 
needsFoo :: Foo a => a -> b 

myFunc = needsFoo (FooBar someBar) 

वैकल्पिक रूप से, आप Bar उदाहरण के लिए एक सामान्य कार्य के साथ foo की जगह, या एक विशेष संस्करण बनाने के साथ द्वारा प्राप्त करने में सक्षम हो सकता है:

-- if every `Foo` is also a `Bar`, you can just do this. No need for `Foo` at all! 
foo :: Bar a => a -> a 
foo = bar . baz 

-- if most `Foo`s aren't `Bar`s, you may be able to use this function when you have a `Bar` 
fooBar :: Bar a => a -> a 
foo = bar . baz 

ये शायद सबसे अच्छा समाधान कर रहे हैं अगर वे के लिए काम आपकी स्थिति।

एक और विकल्प मैन्युअल रूप से प्रत्येक Foo उदाहरण घोषित करना है। यद्यपि कई अलग-अलग कल्पनाशील उदाहरण हो सकते हैं, लेकिन कोडबेस के लिए यह काफी आम है कि वास्तव में केवल कुछ उदाहरण हैं जिनका उपयोग वास्तव में किया जाता है। यदि यह यहां सच है, तो शायद अधिक सामान्य समाधान को लागू करने के बजाय आपको आवश्यक 3 या 4 उदाहरणों को लिखने के लिए शायद कम काम है।

एक बहुत ही अंतिम उपाय के रूप में, आप अपने मूल कोड की तरह कुछ का उपयोग कर सकते हैं, लेकिन आप भी OverlappingInstances जरूरत है यह काम करने के लिए (यदि आप OverlappingInstances की जरूरत नहीं है, तो आप एक Foo वर्ग की जरूरत नहीं है)। यह वह एक्सटेंशन है जो कई उपलब्ध मैचों में जीएचसी को "सबसे विशिष्ट उदाहरण" चुनने की अनुमति देता है। यह कम या ज्यादा काम करेगा, हालांकि आपको वह उम्मीद नहीं मिल सकती है जो आप उम्मीद करते हैं।

class Foo a where 
    foo :: a -> a 

class Bar a where 
    bar :: a -> a 
    baz :: a -> a 

instance Bar String where 
    bar = id 
    baz = id 

instance Bar a => Foo a where 
    foo = bar . baz 

instance Foo [a] where 
    foo _ = [] 

main :: IO() 
main = 
    print (foo "foo") 

अब main एक खाली स्ट्रिंग प्रिंट करता है। a और [a] के लिए दो Foo उदाहरण हैं। उत्तरार्द्ध अधिक विशिष्ट है, इसलिए इसे foo "foo" के लिए चुना जाता है क्योंकि स्ट्रिंग में [Char] टाइप होता है, हालांकि आप शायद पूर्व चाहते थे। तो अब आप भी

instance Foo String where 
    foo = bar . baz 

लिखने के लिए इसके बाद से आप के रूप में अच्छी पूरी तरह से बाहर छोड़ सकते हैं Bar a => Foo a उदाहरण देख सकते हैं।

+0

वाह, विस्तृत उत्तर के लिए धन्यवाद! डिफ़ॉल्ट हस्ताक्षर मेरे लिए काम नहीं करेंगे क्योंकि उन्हें सामान्य वर्ग में डिफ़ॉल्ट घोषित करने की आवश्यकता है, और मैं चाहता हूं कि लोग नए विशिष्ट वर्गों को स्वतंत्र रूप से परिभाषित कर सकें। नया प्रकार का दृष्टिकोण मेरे लिए काम कर सकता है, हालांकि मुझे इसे अपने अधिक जटिल मामले में अनुकूलित करने की आवश्यकता होगी। केवल फ़ंक्शंस का उपयोग करना मेरे लिए काम नहीं करेगा ... ओवरलैपिंग इंस्टेंस भी काम कर सकता है, क्योंकि मेरे मामले में आपके द्वारा वर्णित प्रकार के ओवरलैप का थोड़ा मौका है। यदि यह काम करता है, तो यह सबसे अच्छा होगा क्योंकि यह कम से कम घुसपैठ कर रहा है। एक बार फिर धन्यवाद! –

+0

@ OrenBen-किकी - मैं किसी भी तरह के गर्भ धारण नहीं कर सकता कि एक डिफ़ॉल्ट हस्ताक्षर, 'Foo' के आगे उपयोग को प्रतिबंधित होगा कि क्या यह उदाहरणों, उपवर्गों, या अन्यथा असंबंधित कार्यों में' foo' विधि का उपयोग शामिल है। यह बिल्कुल अस्पष्ट है कि आप यहां मॉडल करने की कोशिश कर रहे हैं, लेकिन मुझे संदेह है कि आप प्रकार कक्षाओं के साथ एक ओओपी-शैली पदानुक्रम स्थापित करने की कोशिश कर रहे हैं। यदि ऐसा है, तो मैं सुझाव दूंगा कि आप Haskell में अपनी समस्या का मॉडलिंग करने के सुझावों के बारे में पूछने के लिए SO पर एक और सवाल पूछें। –

+0

भी चेतावनी का एक शब्द। यदि आप 'ओवरलैपिंग इंस्टेंस' मार्ग पर जाने का निर्णय लेते हैं, तो किसी बिंदु पर जीएचसी शिकायत कर सकता है कि उसे 'इनकोइंटर इंस्टेंस' सक्षम होना चाहिए। ऐसा मत करो, यह मदद नहीं करेगा। –

0

उपरोक्त उत्तर के अतिरिक्त। base लाइब्रेरी से मॉड्यूल में उपयोग की जाने वाली राजनीति आकर्षक है।संक्षेप में, पुस्तकालय बलों में जेनेरिक उदाहरण देने से उपयोगकर्ता आपके निर्णय को स्वीकार कर लेता है, और यह हमेशा करने की सबसे अच्छी बात नहीं है। Data.Traversable में foldMapDefault जैसे फ़ंक्शन शामिल हैं, जो डिफ़ॉल्ट कार्यान्वयन प्रदान करते हैं, लेकिन विशिष्ट कार्यान्वयन का निर्णय अभी भी उपयोगकर्ता तक है।

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