2012-02-20 10 views
5

ठीक है। यहां समस्या बहुत सार है। धैर्य रखने के लिए अनुरोध।संकलक को आश्वस्त करते हुए कि वैध उदाहरण श्रृंखला

मेरे पास "इकाइयों" का एक गुच्छा है, जिसमें प्रत्येक के पास कुछ निश्चित गुण हैं। इन गुणों इस तरह, Seq कक्षा में परिभाषित कर रहे हैं:

class Seq a x y where 
    break :: x -> (a, y) 
    build :: a -> y -> x 

वैचारिक रूप से, a प्रकार महत्वपूर्ण प्रकार, x एक a उत्पन्न करने के लिए इस्तेमाल किया संदर्भ है, और y किसी भी आगे Seq उत्पन्न करने के लिए इस्तेमाल किया संदर्भ है उदाहरणों। breakSeq नीचे तोड़ता है, build आपको इसे पुनर्निर्माण करने की अनुमति देता है।

व्यक्तिगत Seq उदाहरणों पर, यह ठीक काम करता है।

data a :/: b = a :/: b deriving (Eq, Ord) 
infixr :/: 

इस पूरे ऑपरेशन के लक्ष्य Seq उदाहरणों रचना करने में सक्षम होना है: हालांकि, मैं भी एक डेटा निर्माता है कि इस तरह दिखता है।

उदाहरण के लिए, अगर मैं a, b, और cSeq ऐसी है कि a के सभी उदाहरणों है के संदर्भ फ़ीड b में और b के c में फ़ीड, तो मैं अपने आप एक Seq उदाहरण a :/: b :/: c के लिए होना चाहिए। इसे सेट करने के लिए कक्षा रिकर्सिव डेटा प्रकार के माध्यम से काफी सरल है:

instance (Seq a x y, Seq b y z) => Seq (a :/: b) x z where 
    break x = let 
     (a, y) = break x :: (a, y) 
     (b, z) = break y :: (b, z) 
     in (a :/: b, z) 

    build (a :/: b) z = let 
     y = build b z :: y 
     x = build a y :: x 
     in x 

समस्या यह है कि मैं इसका उपयोग नहीं कर सकता। अगर मैं निम्नलिखित तीन Seq उदाहरणों को परिभाषित:

data Foo 
instance Seq Foo Integer Bool 

data Bar 
instance Seq Bar Bool Bar 

data Baz 
instance Seq Baz Bar() 

और एक अच्छी तरह से टाइप किया break समारोह (कार्यान्वयन विवरण संशोधित, और अधिक के लिए here देखें):

myBreak :: Integer -> (Foo :/: Bar :/: Baz) 
myBreak = fst . break' where 
    break' = break :: Integer -> (Foo :/: Bar :/: Baz,()) 

तो मैं नहीं कर सकता यहां तक ​​कि संकलन:

No instances for (Seq Foo Integer y, Seq Bar y y1, Seq Baz y1()) 
    arising from a use of `break' 
Possible fix: 
    add instance declarations for 
    (Seq Foo Integer y, Seq Bar y y1, Seq Baz y1()) 
In the expression: break :: Integer -> (Foo :/: (Bar :/: Baz),()) 
In an equation for break': 
    break' = break :: Integer -> (Foo :/: (Bar :/: Baz),()) 
In an equation for `myBreak': 
    myBreak 
     = fst . break' 
     where 
      break' = break :: Integer -> (Foo :/: (Bar :/: Baz),()) 

यह मुझे लगता है कि myBreak की तरह यह सुनिश्चित नहीं हो सकता है कि Foo → Bar → Baz से संदर्भों का "काम करने वाला धागा" है। मैं संकलक को कैसे समझा सकता हूं कि यह अच्छी तरह से टाइप किया गया है?

यह प्रोग्रामिंग प्रकार में मेरे पहले भ्रमण में से एक है, इसलिए निस्संदेह मैं कुछ अच्छी तरह से स्थापित नियमों को तोड़ रहा हूं। मैं यहां बहुत उत्सुक हूं कि मैं यहां क्या कर रहा हूं, लेकिन मैं अपने लक्ष्यों को बेहतर तरीके से प्राप्त करने के लिए सुझावों के लिए भी खुला हूं।

+1

मैं इस बारे में और अधिक सोचने की कोशिश करूंगा, लेकिन केवल एक अनुमान है, आमतौर पर जब आपके पास "कोई उदाहरण नहीं है ..." त्रुटियों जहां पॉलिफोर्फिक (उदाहरण के लिए 'सीक फू इंटीजर वाई' की तरह है) और आप इसका मतलब यह नहीं था कि कार्यात्मक निर्भरता इसे बेहतर बना सकती है। – Owen

उत्तर

10

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

instance Seq Bar Integer Float 

instance Seq Baz Float() 

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

समाधान किसी प्रकार के प्रकार के स्तर का उपयोग करना है। इसलिए, यदि a महत्वपूर्ण प्रकार है, तो शायद x और y का केवल एक संयोजन है जो a के साथ जाता है। ऐसा करने का एक तरीका functional dependencies के साथ है।

class Seq a x y | a -> x, a -> y where 
    break :: x -> (a, y) 
    build :: a -> y -> x 

कार्यात्मक निर्भरता बहुत सामान्य हैं, और दो एनकोड द्विभाजित प्रकार कार्यों

class Seq a x y | a -> x, a -> y, x y -> a where 
    break :: x -> (a, y) 
    build :: a -> y -> x 

यह बिल्कुल अर्थ विज्ञान आप चाहते क्या हैं पर निर्भर करता है इस्तेमाल किया जा सकता। वैकल्पिक रूप से, आप type families एक्सटेंशन का उपयोग करके थोड़ा अधिक सुरुचिपूर्ण "संबद्ध प्रकार समानार्थी" दृष्टिकोण का उपयोग कर सकते हैं।

class Seq a where 
    type X a 
    type Y a 
    break :: X a -> (a, Y a) 
    build :: a -> Y a -> X a 

instance Seq Bar where 
    type X Bar = Bool 
    type Y Bar = Bar 

यह संस्करण वर्तमान में सबसे मुहावरेदार माना जाता है, और (हालांकि, आप नहीं कर सकते एनकोड द्विभाजन इस तरह से) कार्यात्मक dependancies संस्करण पर कुछ लाभ है। पूर्णता के लिए, यह करने के लिए

type family X a 
type instance X Bar = Bool 

type family Y a 
type instance Y Bar = Bar 

class Seq a where 
    break :: X a -> (a, Y a) 
    build :: a -> Y a -> X a 

instance Seq Bar 

editorialize के बराबर है: हास्केल बाधा बहु पैरामीटर प्रकार वर्गों के साथ तंत्र को सुलझाने prolog की तरह थोड़े एक तर्क की भाषा है। जितना संभव हो सके आपको टाइप स्तर प्रोग्रामिंग के दौरान फ़ंक्शंस का उपयोग करना चाहिए। तीन कारण हैं:

  1. आप एक बाधा के लिए एक समारोह से प्राप्त कर सकते हैं लेकिन ठीक इसके विपरीत
  2. हास्केल Prolog नहीं है। अधिकांश हास्केल प्रोग्रामर कार्यों के बारे में कारणों को आसान पाते हैं। यदि आप प्रोलॉग (या कोई अन्य तर्क भाषा) नहीं जानते हैं, तो कार्य करने के लिए चिपकने के लिए यह अतिरिक्त महत्वपूर्ण है क्योंकि संबंधपरक और कार्यात्मक प्रोग्रामिंग के बीच का अंतर लगभग कार्यात्मक और अनिवार्य प्रोग्रामिंग के बीच अंतर जितना बड़ा है।
  3. समानता के बावजूद, हास्केल की बाधा सॉल्वर प्रोलॉग नहीं है (सहजता से सभी खंडों के सिर के तुरंत बाद कटौती होती है, अगर आप नहीं जानते कि इसका क्या अर्थ है तो चिंता न करें)। रिलेशनशिप टाइप प्रोग्रामिंग पूर्ण बैकट्रैकिंग वाली भाषा में कोडिंग से कहीं अधिक कठिन है।

मेरी सिफारिश दोनों कार्यात्मक निर्भरताओं और type कार्यों को जानती है। टाइप की गई भाषाएं बाकी भाषा के साथ थोड़ा बेहतर तरीके से फिट होती हैं, लेकिन कभी-कभी कार्यात्मक निर्भरता नौकरी के लिए सबसे अच्छा उपकरण होती है।

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