2012-07-29 21 views
6

मैं एक उपयोगिता समारोह है कि एक प्रकार है कि दोनों गणनीय है और घिरा के सभी मानों विश्लेषण करता है के साथ जुड़े Ints की सूची तैयार करें:एक Enum प्रकार

enumerate :: (Enum a, Bounded a) => [a] 
enumerate = [minBound .. maxBound] 

और एक डेटा प्रकार है कि पूर्णांक के लिए मानचित्रण गणनीय प्रकार शामिल :

data Attribute a = Attribute { test :: a -> Int 
          , vals :: [Int] 
          , name :: String } 

कहाँ vals सभी संभव गणनीय मूल्यों का प्रतिनिधित्व करने पूर्णांकों की सूची है। उदाहरण के लिए, अगर मैं

data Foo = Zero | One | Two deriving (Enum,Bounded) 

तो vals[0,1,2] होना होता था।

मैं इन विशेषताओं को प्रोग्रामेटिक रूप से बनाने में सक्षम होना चाहता हूं, बस एक ऐसा फ़ंक्शन दिया गया है जो a को एक गणितीय प्रकार और एक नाम पर मैप करता है। कुछ इस तरह:

attribute :: (Enum b, Bounded b) => (a -> b) -> String -> Attribute a 
attribute f str = Attribute (fromEnum . f) vs str 
    where 
    vs = map fromEnum enumerate 

यह typecheck नहीं करता है, प्रकार हस्ताक्षर में b साथ enumerate करने के लिए कॉल को जोड़ने का कोई रास्ता नहीं है क्योंकि वहाँ। तो मैंने सोचा कि मैं यह कर सकता है:

vs = map fromEnum $ enumerate :: [b] 

लेकिन है कि या तो संकलन नहीं करता है - संकलक का नाम बदलता है कि bb1 करने के लिए। मैं होशियार होने के लिए करने की कोशिश की, GADTs एक्सटेंशन का उपयोग कर:

attribute :: (Enum b, Bounded b, b ~ c) => {- ... -} 
vs = map fromEnum $ enumerate :: (Enum c,Bounded c) => [c] 

लेकिन फिर से, cc1 नाम दिया गया है।

मैं Attribute प्रकार (मुख्य रूप से, क्योंकि मैं b के संभावित विभिन्न मूल्यों के साथ विशेषताओं की सूची संग्रहीत करना चाहते हैं में पैरामीटर के रूप b के प्रकार के शामिल करने के लिए नहीं करना चाहते हैं - यही कारण है कि test टाइप a -> Int है और vals टाइप [Int] है है)।

मैं यह कोड कैसे लिख सकता हूं ताकि यह वही करे जो मैं करना चाहता हूं?

उत्तर

6

प्रकार चर के साथ समस्या यह है कि वे केवल प्रकार हस्ताक्षर में बाध्य हैं। परिभाषा के अंदर प्रकार चर के किसी भी उपयोग से नए, ताजा प्रकार परिवर्तनीय (हालांकि यह प्रकार हस्ताक्षर के समान सटीक नाम है) का संदर्भ लेंगे।

हस्ताक्षर से प्रकार चर के संदर्भ के दो तरीके हैं: ScopedTypeVariables एक्सटेंशन और asTypeOf

ScopedTypeVariables एक प्रकार चर स्पष्ट forall से बंधे के साथ भी परिभाषा में उपलब्ध है, इस प्रकार: हम एक अभिव्यक्ति प्राप्त कर सकते हैं

asTypeOf :: a -> a -> a 
asTypeOf = const 

:

attribute :: forall a b. (Enum b, Bounded b) => 
      (a -> b) -> String -> Attribute a 
attribute f str = Attribute (fromEnum . f) vs str 
    where 
    vs = map fromEnum (enumerate :: [b]) 

दूसरी तरह के समारोह asTypeOf के रूप में परिभाषित शामिल दूसरे पैरामीटर में [b] टाइप करें, एकीकरण यह सुनिश्चित करेगा कि पहले पैरामीटर में [b] टाइप करें।क्योंकि हम f :: a -> b और f undefined :: b है, हम लिख सकते हैं:

attribute :: (Enum b, Bounded b) => (a -> b) -> String -> Attribute a 
attribute f str = Attribute (fromEnum . f) vs str 
    where 
    vs = map fromEnum (enumerate `asTypeOf` [f undefined]) 
+0

Scoped प्रकार चर perfecly काम करता है, धन्यवाद! –

+0

इसके अलावा, यह पहली बार है जब मैंने एक उपयोगी कार्य करने के लिए उपयोग किए गए 'अपरिभाषित' को देखा है। –

+0

@ChrisTaylor: आप निश्चित रूप से 'const' के विभिन्न विशेषज्ञता का उपयोग कर सकते हैं, जैसे' asTypeOf2 :: [b] -> (a -> b) -> [b] 'और फिर आप 'enumerate \' asTypeOf2 लिख सकते हैं \ 'एफ', लेकिन शायद यह प्रयास के लायक नहीं है। – Vitus

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