2011-01-11 6 views
11

के साथ मिश्रित प्रकार चर को समझने के लिए प्रतीत नहीं कर सकता हूं, मैं बहुत सी भाषा को 3/4 समझता हूं, लेकिन हर बार जब मैं अपने कोड में सार्थक तरीके से कक्षाओं का उपयोग करने में अपने पैरों को डुबो देता हूं तो मुझे लगातार प्रवेश मिलता है ।मैं वर्ग

यह बेहद सरल कोड क्यों काम नहीं करता है?

data Room n = Room n n deriving Show 

class HasArea a where 
    width :: (Num n) => a -> n 

instance (Num n) => HasArea (Room n) where 
    width (Room w h) = w 

तो, कमरे की चौड़ाई को स्याही या शायद फ्लोट द्वारा दर्शाया गया है, मैं इस बिंदु पर इसे प्रतिबंधित नहीं करना चाहता हूं। दोनों वर्ग और उदाहरण Nums के एन प्रकार प्रतिबंधित है, लेकिन यह अभी भी यह पसंद नहीं है और मैं इस त्रुटि मिलती है:

Couldn't match expected type `n1' against inferred type `n' 
    `n1' is a rigid type variable bound by 
     the type signature for `width' at Dungeon.hs:11:16 
    `n' is a rigid type variable bound by 
     the instance declaration at Dungeon.hs:13:14 
In the expression: w 
In the definition of `width': width (Room w h) = w 
In the instance declaration for `HasArea (Room n)' 

तो यह मुझे प्रकार से मेल नहीं खाता बताती है, लेकिन यह नहीं है मुझे बताएं कि वे किस तरह के सोचते हैं, जो वास्तव में सहायक होंगे। एक साइड नोट के रूप में, क्या इस तरह की त्रुटि को डीबग करने का कोई आसान तरीका है? ऐसा करने का एकमात्र तरीका यह है कि जब तक यह काम नहीं करता तब तक यादृच्छिक रूप से सामान बदलना है।

उत्तर

18

आपको जो त्रुटि मिल रही है वह आपको बताती है कि यह किस प्रकार का होना चाहिए; दुर्भाग्यवश, दोनों प्रकार प्रकार चर से संकेतित होते हैं, जो इसे देखना कठिन बनाता है। पहली पंक्ति कहती है कि आपने अभिव्यक्ति प्रकार n दिया है, लेकिन यह इसे n1 टाइप करना चाहता था। यह जानने के लिए ये क्या हैं, अगले कुछ लाइनों को देखो:

`n1' is a rigid type variable bound by 
    the type signature for `width' at Dungeon.hs:11:16 

यह कहना है कि n1 एक प्रकार चर जिसका मूल्य में जाना जाता है और इस तरह नहीं बदल सकते हैं ("कठोर" है)। चूंकि यह width के लिए प्रकार हस्ताक्षर से बाध्य है, इसलिए आप जानते हैं कि यह लाइन width :: (Num n) => a -> n से बाध्य है। दायरे में एक और n है, इसलिए यह n का नाम बदलकर n1 (width :: (Num n1) => a -> n1) कर दिया गया है। इसके बाद, हम

`n' is a rigid type variable bound by 
    the instance declaration at Dungeon.hs:13:14 

है यह आपको कह रहा है कि हास्केल लाइन instance (Num n) => HasArea (Room n) where से प्रकार n पाया।रिपोर्ट की जा रही समस्या यह है कि n, जो width (Room w h) = w के लिए गणना की गई जीएचसी प्रकार है, n1 जैसा नहीं है, जो कि यह अपेक्षित प्रकार है।

आपको यह समस्या होने का कारण यह है कि width की आपकी परिभाषा अपेक्षा से कम बहुलक है। width का प्रकार हस्ताक्षर (HasArea a, Num n1) => a -> n1 है, जिसका अर्थ है कि प्रत्येक प्रकार के लिए जो HasArea का उदाहरण है, आप प्रकार की संख्या के साथ इसकी चौड़ाई का प्रतिनिधित्व कर सकते हैं। हालांकि, आपकी आवृत्ति परिभाषा में, लाइन width (Room w h) = w का अर्थ है कि width में Num n => Room n -> n है। ध्यान दें कि यह पर्याप्त रूप से polymorphic नहीं है: जबकि Room nHasArea का एक उदाहरण है, (Num n, Num n1) => Room n -> n1 टाइप करने के लिए width की आवश्यकता होगी। यह सामान्य n को सामान्य n1 के साथ एकीकृत करने में असमर्थता है जो आपकी प्रकार की त्रुटि उत्पन्न कर रहा है।

इसे ठीक करने के कुछ तरीके हैं। एक दृष्टिकोण (और शायद सबसे अच्छा तरीका), जिसे आप sepp2k's answer में देख सकते हैं HasArea को * -> * प्रकार का एक प्रकार चर लेना है; इसका मतलब है कि a एक प्रकार के होने के बजाय, a Int या a n जैसी चीजें हैं। Maybe और []* -> * के प्रकार के प्रकार हैं। (सामान्य प्रकार जैसे Int या Maybe Double में * है।) यह शायद सबसे अच्छा शर्त है।

आप जो एक क्षेत्र (जैसे, data Space = Space (Maybe Character), जहां width हमेशा 1 है) तरह * के कुछ प्रकार है, तथापि, वह काम नहीं करेंगे। width, आप प्रकार वर्ग खुद को पैरामीटर के रूप चौड़ाई के प्रकार के पारित

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-} 

data Room n = Room n n deriving Show 

class Num n => HasArea a n where 
    width :: a -> n 

instance Num n => HasArea (Room n) n where 
    width (Room w h) = w 

अब, इसलिए: एक और तरीका है (जो Haskell98/Haskell2010 के लिए कुछ एक्सटेंशन की आवश्यकता है) HasArea एक बहु पैरामीटर प्रकार वर्ग बनाने के लिए है (HasArea a n, Num n) => a -> n प्रकार है। हालांकि, इसका एक संभावित नकारात्मक पक्ष यह है कि आप instance HasArea Foo Int और instance HasArea Foo Double घोषित कर सकते हैं, जो समस्याग्रस्त हो सकता है। यदि ऐसा है, तो इस समस्या को हल करने के लिए, आप कार्यात्मक निर्भरताओं का उपयोग कर सकते हैं या परिवारों को टाइप कर सकते हैं। कार्यात्मक निर्भरता आपको यह निर्दिष्ट करने की अनुमति देती है कि एक प्रकार दिया गया है, अन्य प्रकार विशिष्ट रूप से निर्धारित हैं, जैसे कि आपके पास सामान्य कार्य था। उन का उपयोग करना कोड

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FunctionalDependencies #-} 

data Room n = Room n n deriving Show 

class Num n => HasArea a n | a -> n where 
    width :: a -> n 

instance Num n => HasArea (Room n) n where 
    width (Room w h) = w 

थोड़ी जानकारी देता है GHC कि अगर यह a अनुमान लगा सकते हैं, तो यह भी n अनुमान लगा सकते हैं, वहाँ के बाद से केवल एक n हर a के लिए देता है। यह ऊपर चर्चा की गई घटनाओं के प्रकार को रोकता है।

प्रकार परिवारों अधिक अलग हैं:

{-# LANGUAGE MultiParamTypeClasses, FlexibleContexts, TypeFamilies #-} 

data Room n = Room n n deriving Show 

class Num (Area a) => HasArea a where 
    type Area a :: * 
    width :: a -> Area a 

instance Num n => HasArea (Room n) where 
    type Area (Room n) = n 
    width (Room w h) = w 

यह कहते हैं एक width समारोह होने के अलावा में, HasArea वर्ग भी एक Areaप्रकार (या प्रकार कार्य है कि आप इसके बारे में सोचने के लिए चाहते हैं, तो उस तरफ)। प्रत्येक HasArea a के लिए, आप निर्दिष्ट करते हैं कि Area a किस प्रकार है (जो सुपरक्लस बाधा के लिए धन्यवाद, Num का उदाहरण होना चाहिए), और तब उस प्रकार का उपयोग अपनी तरह की संख्या के रूप में करें।

इस तरह की त्रुटियों को कैसे डिबग करना है?ईमानदारी से, मेरी सबसे अच्छी सलाह है "अभ्यास, अभ्यास, अभ्यास।" समय के साथ, आप यह पता लगाने के लिए और अधिक उपयोग करेंगे (ए) त्रुटियां क्या कह रही हैं, और (बी) शायद गलत क्या हुआ। यादृच्छिक रूप से सामान बदलना सीखना एक तरीका है। सलाह देने का सबसे बड़ा टुकड़ा, हालांकि, Couldn't match expected type `Foo' against inferred type `Bar' लाइनों पर ध्यान देना है। ये आपको बताते हैं कि कंपाइलर की गणना किस प्रकार की गई है (Bar) और अपेक्षित (Foo) इस प्रकार के लिए, और यदि आप सटीक रूप से यह पता लगा सकते हैं कि कौन से प्रकार हैं, तो यह पता लगाने में आपकी सहायता करता है कि त्रुटि कहां है।

+0

वाह, आपने वास्तव में पार्क से बाहर खटखटाया। मैं इन सटीक विषयों पर जा रहा था क्योंकि मैंने तुरंत इसे कई प्रकार के प्रकारों के लिए उपयोग करने की क्षमता की कमी देखी। इतना ही नहीं, मैंने मल्टी पैरा टाइपक्लास को याद किया जो कि उनमें से सबसे आसान है। धन्यवाद। –

+0

+1 मुझे लगता है कि मल्टीपार्मेटर टाइपक्लास + फंडप्स यहां सबसे सरल दृष्टिकोण है। –

+0

+1 शानदार जवाब! – asm

9
class HasArea a where 
    width :: (Num n) => a -> n 

प्रकार (Num n) => a -> n मतलब यह है कि किसी भी प्रकार n जो Num का एक उदाहरण है के लिए, width उस प्रकार के मान देने के लिए सक्षम होना चाहिए। इसलिए किसी भी मान प्रकार T की v, जहां THasArea का एक उदाहरण निम्नलिखित कोड मान्य होना चाहिए है के लिए:

let x :: Integer = width v 
    y :: Double = width v 
in 
    whatever 

हालांकि यह आपके Room उदाहरण के लिए ऐसा नहीं है। उदाहरण के लिए Room Integery :: Dobule = width v मान्य नहीं है।

अपने उदाहरण काम आप कुछ इस तरह कर सकता करने के लिए:

data Room n = Room n n deriving Show 

class HasArea a where 
    width :: (Num n) => a n -> n 

instance HasArea Room where 
    width (Room w h) = w 

यहाँ हम नहीं कहते कि Room Integer, Room Float आदि HasArea की घटनाएं होती हैं, लेकिन इसके बजाय RoomHasArea का एक उदाहरण है। और width का प्रकार यह है कि के मान दिए जाने पर यह n प्रकार का मान बनाता है, इसलिए यदि आप Room Integer डालते हैं, तो आप Integer वापस लेते हैं। इस तरह प्रकार फिट बैठता है।

+0

मैं देखता हूं, धन्यवाद। यह समझ आता है। –

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