2011-07-13 9 views
9

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

class Nameable a where 
    name :: a -> String 

data Human = Human { 
    firstName :: String 
} 

data Building = Building { 
    buildingName :: String 
} 

instance Nameable Human where 
    name x = firstName x 

instance Nameable Building where 
    -- I think the x is redundant here, i.e the following should work: 
    -- name = buildingName 
    name x = buildingName x 

main :: IO() 
main = do 
    putStr $ show (map name items) 
    where 
    items :: (Nameable a) => [a] 
    items = [ Human{firstName = "Don"} 
      -- Ideally I want the next line in the array too, but that gives an 
      -- obvious type error at the moment. 
      --, Building{buildingName = "Empire State"} 
      ] 

यह संकलन नहीं करता है::

TypeTest.hs:23:14: 
    Couldn't match expected type `a' against inferred type `Human' 
     `a' is a rigid type variable bound by 
      the type signature for `items' at TypeTest.hs:22:23 
    In the expression: Human {firstName = "Don"} 
    In the expression: [Human {firstName = "Don"}] 
    In the definition of `items': items = [Human {firstName = "Don"}] 

मैं अपेक्षा की होगी instance Nameable Human अनुभाग इस काम होगा यहाँ मैं अब तक है। क्या कोई यह समझा सकता है कि मैं क्या गलत कर रहा हूं, और बोनस के लिए क्या मैं "अवधारणा" काम करने की कोशिश कर रहा हूं, क्योंकि मुझे यह जानने में परेशानी हो रही है कि क्या खोजना है।

This question समान लगता है, लेकिन मैं अपनी समस्या के साथ कनेक्शन को समझ नहीं पाया।

items :: (Nameable a) => [a] 

यह कह रहा है कि किसी भी प्रकार के लिए Nameable, items मुझे उस प्रकार की एक सूची दे देंगे:

+0

यदि आप 'आइटम्स' के लिए टाइप-एनोटेशन हटाते हैं तो यह काम करता है ... हालांकि, आइटमों में '[मानव]' टाइप है (जो शायद आप नहीं चाहते हैं?) – phynfo

उत्तर

13

items के प्रकार पर विचार करें। यह कहता है कि items एक ऐसी सूची है जिसमें अलग-अलग Nameable प्रकार हो सकते हैं, जैसा कि आप सोच सकते हैं। आप items :: [exists a. Nameable a => a] जैसे कुछ चाहते हैं, सिवाय इसके कि आपको एक रैपर प्रकार शुरू करने और forall का उपयोग करने की आवश्यकता होगी। (देखें: Existential type)

{-# LANGUAGE ExistentialQuantification #-} 

data SomeNameable = forall a. Nameable a => SomeNameable a 

[...] 

items :: [SomeNameable] 
items = [ SomeNameable $ Human {firstName = "Don"}, 
      SomeNameable $ Building {buildingName = "Empire State"} ] 

SomeNameable का डेटा निर्माता में परिमाणक मूल रूप से यह जो a प्रयोग किया जाता है सिवाय इसके कि इसे Nameable है कि, के बारे में वास्तव में सब कुछ भूल जाते हैं अनुमति देता है। इसलिए, आपको तत्वों पर Nameable कक्षा से फ़ंक्शंस का उपयोग करने की अनुमति दी जाएगी।

इस अच्छे उपयोग करने के लिए बनाने के लिए, आप आवरण के लिए एक उदाहरण बना सकते हैं:

instance Nameable (SomeNameable a) where 
    name (SomeNameable x) = name x 

अब आप इसे इस तरह उपयोग कर सकते हैं:

Main> map name items 
["Don", "Empire State"] 
+2

मेरी इच्छा है कि जीएचसी में अस्तित्व में मात्रा न हो । इससे मदद मिलती है इससे ज्यादा भ्रमित होता है। प्रश्न: 'कुछ नामांकित' और 'स्ट्रिंग'' के बीच क्या अंतर है? – luqui

+2

@luqui: इस मामले में, के बाद से 'name' केवल आपरेशन उपलब्ध क्या आप इसे भी सीधे लागू हो सकता है! आप केवल कभी '' String's SomeNameable' की एक सूची से बाहर निकलने के लिए जा रहे हैं। 'चेंजनाम :: स्ट्रिंग -> ए -> ए' से' नामनीय 'जैसे संयोजन संयोजकों का परिचय दें और चीजें दिलचस्प हो जाएं! – yatima2975

+0

धन्यवाद, जो मेरे प्रश्न का उत्तर देता है। अब मुझे यह पता लगाने की ज़रूरत है कि यह मेरी वास्तविक समस्या के लिए उपयुक्त है या मैं संघ के प्रकार से दूर हो सकता हूं। –

3

आप existentially Quanitified प्रकार का उपयोग करने की कोशिश कर सकते और यह इस तरह कार्य करें:

data T = forall a. Nameable a => MkT a 
items = [MkT (Human "bla"), MkT (Building "bla")] 
6

मैं @ Hammar का जवाब चाहते हैं, और आप भी +०१२३८९०६१७१८ की जांच करनी चाहिएजो एक और उदाहरण प्रदान करता है।

लेकिन, आप अपने प्रकार के बारे में अलग-अलग सोचना चाहेंगे। Nameable का मुक्केबाजी SomeNameable डेटा प्रकार में आमतौर पर मुझे इस बारे में सोचने लगती है कि विशिष्ट मामले के लिए यूनियन प्रकार सार्थक है या नहीं।

data Entity = H Human | B Building 
instance Nameable Entity where ... 

items = [H (Human "Don"), B (Building "Town Hall")] 
+2

आप केवल 'आइटम्स = ["डॉन", "टाउन हॉल"] का उपयोग कर सकते हैं, क्योंकि इसमें बिल्कुल वही जानकारी है। – luqui

+0

@luqui यह आपको 'एंटिटी' कन्स्ट्रक्टर पर पैटर्न मिलान करने की अनुमति देता है, इसलिए इसमें केवल स्ट्रिंग की तुलना में अधिक जानकारी होती है। हालांकि 'मानव' और 'बिल्डिंग' शायद अनावश्यक हैं। – Alex

+0

ओह व्हाउप्स। मैंने कर्सर को अस्तित्व के संकेतों को देखा और विस्तार से नहीं पढ़ा। – luqui

5

मुझे यकीन है कि तुम क्यों एक Human का नाम और एक Building के नाम हो रही के लिए एक ही समारोह का उपयोग करना चाहते नहीं हूँ।

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

लेकिन अगर एक नाम होने अपने कार्यक्रम के पूरा उद्देश्य के बारे में कुछ महत्वपूर्ण है, और एक Human और एक Building वास्तव में काफी है कि संबंध में एक ही बात जहाँ तक अपने कार्यक्रम के रूप में संबंध है कर रहे हैं, तो क्या तुम करोगी अपने प्रकार एक साथ निर्धारित किए हैं:

data NameableThing = 
    Human { name :: String } | 
    Building { name :: String } 

है कि आप एक बहुरूपी समारोह name उस प्रकार वर्गों में पाने के लिए जरूरत के बिना जो कुछ NameableThing की विशेष स्वाद आप हैं तो आप लिए काम करता है देता है।

आमतौर पर आप स्थिति की एक अलग तरह के लिए एक प्रकार वर्ग का प्रयोग करेंगे: आप एक ही उद्देश्य लेकिन विभिन्न प्रकार के लिए एक अलग कार्यान्वयन है कि गैर तुच्छ आपरेशन के कुछ प्रकार है। फिर भी, इसके बजाय किसी अन्य दृष्टिकोण का उपयोग करना बेहतर होता है, जैसे पैरामीटर के रूप में फ़ंक्शन को पास करना (एक "उच्च ऑर्डर फ़ंक्शन" या "एचओएफ")।

हास्केल प्रकार कक्षाएं एक सुंदर और शक्तिशाली उपकरण हैं, लेकिन वे पूरी तरह से ऑब्जेक्ट उन्मुख भाषाओं में "कक्षा" कहलाते हैं, से अलग हैं और इन्हें बहुत कम बार उपयोग किया जाता है।

और मैं निश्चित रूप से एक उन्नत एक्सटेंशन का उपयोग कर की तरह अस्तित्व योग्यता सिर्फ एक वस्तु उन्मुख डिजाइन पैटर्न में फिट Haskell करने के द्वारा अपने कार्यक्रम उलझी सलाह नहीं देते।

+0

क्षमा करें, मेरा उदाहरण दूषित है। यदि आप उत्सुक हैं, तो मेरा पूरा कार्यक्रम यहां है: https://github.com/xaviershay/xtdo-hs/blob/master/Xtdo.hs मुझे इसी तरह से 'कार्य' और 'आवर्ती टास्क परिभाषा' का इलाज करने में सक्षम होना चाहिए, ध्यान दें कि 'DeleteTaskByName' और' deleteRecurringByName' बहुत अधिक फ़ंक्शन हैं। –

8

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

नोट करने वाली पहली बात यह है कि हास्केल में कोई डाउनकास्टिंग नहीं है।

data SomeNameable = forall a. Nameable a => SomeNameable a 

फिर जब आप एक वस्तु

foo :: SomeNameable 
foo = SomeNameable $ Human { firstName = "John" } 

जानकारी जो ठोस प्रकार के बारे में वस्तु के साथ (यहाँ Human) हमेशा के लिए खो दिया है बनाया गया था बनाने के लिए: यही कारण है, अगर आप निम्न अस्तित्व का उपयोग करें। केवल एक चीज जिन्हें हम जानते हैं: यह कुछ प्रकार a है, और Nameable a उदाहरण है।

ऐसी जोड़ी के साथ क्या करना संभव है? खैर, आप a के name प्राप्त कर सकते हैं, और ... यह है। यही सब है इसके लिए। वास्तव में, एक आइसोमोर्फिज्म है। मैं एक नया डेटा प्रकार बनाउंगा ताकि आप देख सकें कि इस आइसोमोर्फिज्म के मामलों में कैसा होता है जब आपकी सभी ठोस वस्तुओं में वर्ग की तुलना में अधिक संरचना होती है।

data ProtoNameable = ProtoNameable { 
    -- one field for each typeclass method 
    protoName :: String 
} 

instance Nameable ProtoNameable where 
    name = protoName 

toProto :: SomeNameable -> ProtoNameable 
toProto (SomeNameable x) = ProtoNameable { protoName = name x } 

fromProto :: ProtoNameable -> SomeNameable 
fromProto = SomeNameable 

हम देख सकते हैं, इस कल्पना अस्तित्व प्रकार SomeNameableProtoNameable रूप में एक ही संरचना और जानकारी है, जो String isomorphic को है, इसलिए जब आप इस उदात्त अवधारणा SomeNameable उपयोग कर रहे हैं, तो आप वास्तव में सिर्फ String में कह रहे हैं एक शांत तरीका। तो क्यों न सिर्फ String कहें?

items = [ "Don", "Empire State" ] 

मैं इस "protoization" के बारे में कुछ नोट्स जोड़ने चाहिए: यह जब typeclass आप existentially से अधिक मात्र निर्धारण कर रहे हैं केवल के रूप में इस के रूप में सरल है

आपका items परिभाषा इस परिभाषा के रूप में बिल्कुल एक ही जानकारी है एक निश्चित संरचना है: अर्थात् जब यह ओओ कक्षा की तरह दिखता है।

class Foo a where 
    method1 :: ... -> a -> ... 
    method2 :: ... -> a -> ... 
    ... 

है यही कारण है कि प्रत्येक विधि केवल a एक बार एक तर्क के रूप उपयोग करता है। आप की तरह Num

class Num a where 
    (+) :: a -> a -> a 
    ... 

जो कई तर्क पदों में a का उपयोग करता है, या एक परिणाम के रूप में कुछ है, तो अस्तित्व को नष्ट करने के रूप में नहीं आसान, but still possible है। हालांकि मेरी सिफारिश इस जटिलता और दो अभ्यावेदन के सुदूर संबंधों की वजह से एक सूक्ष्म संदर्भ पर निर्भर पसंद करने के लिए एक हताशा से बदल जाता है, क्या करना है। हालांकि, हर बार मैं अभ्यास यह tyepclass, जहां यह केवल अनावश्यक जटिलता कहते हैं की Foo प्रकार के साथ है में प्रयोग किया जाता existentials देखा, तो मैं काफी जोरदार ढंग से consider it an antipattern है। इन मामलों मैं अपने codebase से पूरी क्लास को नष्ट करने और विशेष रूप से protoized प्रकार का उपयोग (के बाद आप यह एक अच्छा नाम देना) की सिफारिश के अधिकांश में।

इसके अलावा, यदि आप को डाउनकास्ट करने की आवश्यकता है, तो अस्तित्व आपके आदमी नहीं हैं। आप या तो बीजगणितीय डेटा प्रकार का उपयोग कर सकते हैं, जैसा कि अन्य लोगों ने उत्तर दिया है, या आप Data.Dynamic (जो मूल रूप से Typeable से अधिक अस्तित्व में है) का उपयोग कर सकते हैं। लेकिन ऐसा न करें; Dynamic का उपयोग करने वाला एक हास्केल प्रोग्रामर अनजान है। एक एडीटी रास्ता है जाने के लिए, जहां आप एक ही स्थान पर सभी संभव प्रकारों को चिह्नित कर सकते हैं (जो आवश्यक है ताकि "डाउनकास्टिंग" करने वाले कार्यों को पता चले कि वे सभी संभावित मामलों को संभालते हैं)

0

मैंने अभी देखा है ।

data Task 
    = Once 
     { name :: String 
     , scheduled :: Maybe Day 
     , category :: TaskCategory 
     } 
    | Recurring 
     { name :: String 
     , nextOccurrence :: Day 
     , frequency :: RecurFrequency 
     } 
type ProgramData = [Task] -- don't even need a new data type for this any more 
: the code पर कि इस सवाल से सार संक्षेप है इस के लिए, मैं Task और RecurringTaskDefinition प्रकार विलय की सिफारिश करेंगे

फिर, name फ़ंक्शन किसी भी प्रकार पर ठीक काम करता है, और जिन कार्यों के बारे में आप शिकायत कर रहे थे deleteTask और deleteRecurring को भी मौजूद होने की आवश्यकता नहीं है - आप सामान्य रूप से मानक delete फ़ंक्शन का उपयोग कर सकते हैं।

+0

सुझाव के लिए धन्यवाद। उस समय के लिए जब मैं एक उच्च-आदेश समारोह का उपयोग कर प्राप्त कर चुका हूं, लेकिन मुझे लगता है कि मैं यह देखने के लिए भी कोशिश करूंगा कि यह कैसे गिरता है। धन्यवाद। –

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

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