2009-12-13 18 views
11

मान लीजिए कि मेरे पास दो डेटा प्रकार हैं Foo और Bar। फू में फ़ील्ड एक्स और वाई है। बार में फ़ील्ड एक्स और जेड है। मैं एक ऐसा फ़ंक्शन लिखने में सक्षम होना चाहता हूं जो पैरामीटर या बार को पैरामीटर के रूप में लेता है, एक्स मान निकालता है, उस पर कुछ गणना करता है, और उसके बाद x मान सेट के साथ एक नया Foo या Bar देता है।हास्केल रिकॉर्ड वाक्यविन्यास और प्रकार वर्ग

class HasX a where 
    getX :: a -> Int 
    setX :: a -> Int -> a 

data Foo = Foo Int Int deriving Show 

instance HasX Foo where 
    getX (Foo x _) = x 
    setX (Foo _ y) val = Foo val y 

getY (Foo _ z) = z 
setY (Foo x _) val = Foo x val 

data Bar = Bar Int Int deriving Show 

instance HasX Bar where 
    getX (Bar x _) = x 
    setX (Bar _ z) val = Bar val z 

getZ (Bar _ z) = z 
setZ (Bar x _) val = Bar x val 

modifyX :: (HasX a) => a -> a 
modifyX hasX = setX hasX $ getX hasX + 5 

समस्या यह है कि उन सभी getters और setters लिखने के लिए दुखदाई है, खासकर अगर मैं खेतों के बहुत सारे है कि वास्तविक दुनिया डेटा प्रकार के साथ फू और बार की जगह,:

यहाँ एक दृष्टिकोण है।

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

data Foo = Foo {x :: Int, y :: Int} deriving Show 
data Bar = Foo {x :: Int, z :: Int} deriving Show 

जैसे रिकॉर्ड को परिभाषित करने का प्रयास करता हूं तो मुझे एक त्रुटि मिल जाएगी कि x को कई बार परिभाषित किया गया है। और, मैं एक प्रकार के वर्ग के इन हिस्सों को बनाने के लिए कोई रास्ता नहीं देख रहा हूं ताकि मैं उन्हें संशोधित करने के लिए पास कर सकूं।

क्या इस समस्या को हल करने का कोई अच्छा साफ तरीका है, या क्या मैं अपने स्वयं के गेटर्स और सेटर्स को परिभाषित करने के साथ अटक गया हूं? एक और तरीका रखो, क्या टाइप क्लास (दोनों गेटर्स और सेटर्स) के साथ रिकॉर्ड सिंटैक्स द्वारा बनाए गए कार्यों को जोड़ने का कोई तरीका है?

संपादित

यहाँ वास्तविक समस्या मैं हल करने के लिए कोशिश कर रहा हूँ है। मैं संबंधित प्रोग्रामों की एक श्रृंखला लिख ​​रहा हूं जो सभी अपने कमांड लाइन विकल्पों को पार्स करने के लिए System.Console.GetOpt का उपयोग करते हैं। इन कार्यक्रमों में बहुत सारे कमांड लाइन विकल्प होंगे, लेकिन कुछ कार्यक्रमों में अतिरिक्त विकल्प हो सकते हैं। मैं चाहता हूं कि प्रत्येक प्रोग्राम अपने सभी विकल्प मान वाले रिकॉर्ड को परिभाषित करने में सक्षम हो। इसके बाद मैं एक डिफ़ॉल्ट रिकॉर्ड मान से शुरू होता हूं जिसे कमांड लाइन तर्कों को प्रतिबिंबित करने वाला अंतिम रिकॉर्ड प्राप्त करने के लिए एक स्टेटटी मोनड और गेटऑप्ट के माध्यम से परिवर्तित किया जाता है। एक कार्यक्रम के लिए, यह दृष्टिकोण वास्तव में अच्छी तरह से काम करता है, लेकिन मैं सभी कार्यक्रमों में कोड का पुन: उपयोग करने का एक तरीका खोजने का प्रयास कर रहा हूं।

+3

यदि आपके पास एक ही डेटा प्रकार 'डेटा FooBar = Foo {x :: Int, y :: Int} था। बार {x :: Int, z :: Int} 'आपको यह समस्या नहीं होगी। यदि आपके डेटा प्रकार अलग-अलग मॉड्यूल में थे तो आप '{- # LANGUAGE DisambiguateRecordFields # -} 'का उपयोग कर सकते हैं। आपके वर्तमान डिजाइन के साथ चिपकने के लिए कोई कारण? – ephemient

उत्तर

5

आप extensible records चाहते हैं, जो मैं इकट्ठा करता हूं, हास्केल में सबसे ज्यादा विषयों में से एक है। ऐसा प्रतीत होता है कि वर्तमान में इसे लागू करने के तरीके पर बहुत आम सहमति नहीं है।

आपके मामले में ऐसा लगता है कि एक सामान्य रिकॉर्ड के बजाय आप HList में लागू की गई एक विषम सूची का उपयोग कर सकते हैं।

फिर, ऐसा लगता है कि आपके पास केवल दो स्तर हैं: आम और कार्यक्रम। इसलिए शायद आपको सामान्य विकल्पों के लिए एक सामान्य रिकॉर्ड प्रकार और प्रत्येक प्रोग्राम के लिए एक प्रोग्राम-विशिष्ट रिकॉर्ड प्रकार परिभाषित करना चाहिए, और उन प्रकार के टुपल पर स्टेटटी का उपयोग करना चाहिए। सामान्य सामानों के लिए आप उन उपनामों को जोड़ सकते हैं जो आम एक्सेसर्स के साथ fst लिखते हैं, इसलिए यह कॉलर्स के लिए अदृश्य है।

+0

एक सामान्य रिकॉर्ड प्रकार और प्रोग्राम-विशिष्ट प्रकार होने के बारे में दिलचस्प विचार। मेरे पास सामान्य और कार्यक्रम के अलावा समानता के अधिक समूह हो सकते हैं (अभी तक सुनिश्चित नहीं है), लेकिन मैं इसका समर्थन करने के लिए आसानी से अपना दृष्टिकोण बढ़ा सकता हूं। (उदाहरण के लिए, मैं स्टेटटी के माध्यम से रिकॉर्ड की सहयोगी सूची के रूप में पास कर सकता हूं, और प्रत्येक विकल्प को पता होगा कि नाम से सूची में इसके संबंधित रिकॉर्ड को कैसे देखना है।) –

3

आप

data Foo = Foo { fooX :: Int, fooY :: Int } deriving (Show) 
data Bar = Bar { barX :: Int, barZ :: Int } deriving (Show) 

instance HasX Foo where 
    getX = fooX 
    setX r x' = r { fooX = x' } 

instance HasX Bar where 
    getX = barX 
    setX r x' = r { barX = x' } 

के रूप में इस तरह के कोड का उपयोग कर आप अपने कोड में मॉडलिंग कर रहे हैं? अगर हम इस समस्या के बारे में अधिक जानते थे, तो हम इस ऑब्जेक्ट-ओरिएंटेड डिज़ाइन की तुलना में कम अजीब सुझाव दे सकते हैं जो एक कार्यात्मक भाषा में shoehorned।

+0

प्रतिक्रिया के लिए धन्यवाद। आपका दृष्टिकोण कुछ हद तक चीजों को सरल बनाता है। मैंने अपने मूल प्रश्न को संशोधित करने के लिए वास्तविक समस्या के बारे में और अधिक व्याख्या करने के लिए संशोधित किया है। मैं मानता हूं कि एक कार्यात्मक भाषा के साथ एक ओओ दृष्टिकोण का उपयोग आमतौर पर सबसे अच्छा तरीका नहीं है। मैं कार्यात्मक भाषाओं में नया हूं और अभी भी अपनी ओओ सोच से बाहर निकलने की कोशिश कर रहा हूं। :-) –

1

यदि आप फोल्डबल के प्रकार के उदाहरण बनाते हैं तो आपको एक टाइलिस्ट फ़ंक्शन मिलता है जिसे आप अपने एक्सेसर के आधार के रूप में उपयोग कर सकते हैं।

यदि फोल्डबल आपके द्वारा कुछ भी नहीं करता है, तो हो सकता है कि सही तरीका एक इंटरफ़ेस के रूप में इच्छित इंटरफ़ेस को परिभाषित करना है और व्युत्पन्न मानों को स्वत: उत्पन्न करने का एक अच्छा तरीका है।

शायद

deriving(Data) 

करने से आप अपनी पहुंच को बंद आधार पर gmap combinators इस्तेमाल कर सकते हैं से पाने से।

2

मुझे जेनेरिक के लिए नौकरी की तरह लगता है। आप अलग अलग newtypes के साथ अपने इंट टैग कर सकते हैं, तो आप लिखने में सक्षम हो जाएगा (uniplate साथ, मॉड्यूल PlateData):

data Foo = Foo Something Another deriving (Data,Typeable) 
data Bar = Bar Another Thing deriving (Data, Typerable) 

data Opts = F Foo | B Bar 

newtype Something = S Int 
newtype Another = A Int 
newtype Thing = T Int 

getAnothers opts = [ x | A x <- universeBi opts ] 

यह कहीं भी देता है, जिसमें अंदर से सभी एक अन्य के निकालने होगा।

संशोधन भी संभव है।

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