2015-02-23 6 views
5

{-# LANGUAGE LambdaCase #-}टाइपक्लास के उदाहरणों के रूप में कार्य?

मेरे पास कई तरीकों से विफलता को एन्कोड करने वाले कार्यों का एक समूह है।

  • f :: A -> Bool रिटर्न विफलता पर False विफलता पर
  • g :: B -> Maybe B' रिटर्न Nothing
  • h :: C -> Either Error C' रिटर्न Left ... विफलता पर

मैं श्रृंखला के लिए Maybe इकाई के रूप में एक ही तरीके से इन आपरेशनों हैं: उदाहरण के लिए , इसलिए चेनिंग फ़ंक्शन को यह जानने की आवश्यकता है कि प्रत्येक फ़ंक्शन अगले पर जाने से पहले विफल रहा है या नहीं। इस के लिए मैं इस वर्ग ने लिखा है:

class Fail a where 
    isFail :: a -> Bool 
instance Fail() where 
    isFail() = False 
instance Fail Bool where -- a 
    isFail = not 
instance Fail (Maybe a) where -- b 
    isFail = not . isJust 
instance Fail (Either a b) where -- c 
    isFail (Left _) = True 
    isFail _ = False 

हालांकि, यह संभव है कि कार्यों कि अनुरूप नहीं है मौजूद हैं:

  • f' :: A -> Bool रिटर्न विफलता पर True विफलता पर
  • g' :: B -> Maybe Error रिटर्न Just Error (Nothing सफलता पर)
  • h' :: C -> Either C' Error विफलता पर

ये बस उन्हें कार्यों है कि उन्हें बदलने, उदाहरण के लिए के साथ लपेटकर द्वारा ठीक किया जा सकता है:

  • f'' = not . f'
  • g'' = (\case Nothing -> Right(); Just e -> Left e) . g'
  • h'' = (\case Left c -> Right c; Right e -> Left e) . h'

हालांकि, श्रृंखलन समारोह के उपयोगकर्ता गठबंधन f, g, h, f', g', और h' और उन्हें बस काम करने में सक्षम हो जाता है। वह नहीं जानता कि फ़ंक्शन के रिटर्न प्रकार को तब तक परिवर्तित करने की आवश्यकता है जब तक कि वह प्रत्येक संयोजन के अर्थशास्त्र को देखता न हो, और जांच करें कि क्या वे Fail के दायरे में हैं जो उनके पास हैं। औसत उपयोगकर्ता के लिए यह भी कठिन और बहुत सूक्ष्म है, विशेष रूप से उपयोगकर्ता को सही उदाहरण चुनने के लिए बाईपास करने के प्रकार के साथ।

ये कार्य इस ज्ञान के साथ नहीं बनाए गए थे कि उनका उपयोग कैसे किया जाएगा। तो मैं एक प्रकार data Result a b = Fail a | Success b बना सकता हूं और प्रत्येक फ़ंक्शन के चारों ओर रैपर बना सकता हूं।उदाहरण के लिए:

  • fR = (\case True -> Sucess(); False -> Fail()) . f
  • f'R = (\case False -> Sucess(); True -> Fail()) . f'
  • gR = (\case Just a -> Sucess a; Nothing -> Fail()) . g
  • g'R = (\case Nothing -> Sucess(); Just e -> Fail e) . g'
  • hR = (\case Left e -> Fail e; Right a -> Sucess a) . h
  • h'R = (\case Right e -> Fail e; Left a -> Sucess a) . h'

बहरहाल, यह गंदा लगता है। हम जो कर रहे हैं वह सिर्फ f, g, h, f', g', और h' संयोजन संयोजन के संदर्भ में उपयोग किया जाता है। क्या ऐसा करने का अधिक सीधा तरीका है? क्या मैं वास्तव में चाहते हैं Fail typeclass का जो उदाहरण प्रत्येक कार्य के लिए इस्तेमाल किया जाना चाहिए, यानी, fa, gb, hc, और f' (ऊपर typeclass उदाहरणों को दिए गए नामों का प्रयोग करके), कहने का एक तरीका है → a', g'b', h'c' "अवैध" कार्यों के लिए है, जहां a', b', और c' निम्नलिखित उदाहरणों (जो पिछले अभियानों ओवरलैप के रूप में परिभाषित कर रहे हैं, ताकि आप उन्हें नाम से लेने के लिए सक्षम होना चाहिए था किसी भी तरह):

instance Fail Bool where -- a' 
    isFail = id 
instance Fail (Maybe a) where -- b' 
    isFail = isJust 
instance Fail (Either a b) where -- c' 
    isFail (Right _) = True 
    isFail _ = False 

हालांकि टाइपक्लास के माध्यम से इसे जरूरी नहीं है। शायद टाइपक्लास के अलावा ऐसा करने का कोई तरीका है?

+1

यदि आप उन्हें एक मोनड ('डू' नोटेशन के साथ) की तरह एक साथ श्रृंखला बनाना चाहते हैं तो आप उन्हें सभी को एक ही प्रकार में बदलने की आवश्यकता है जिसके बाद आप एक मोनाड उदाहरण बना सकते हैं। आप बताते हैं कि आप टाइप सिस्टम को केवल यह समझने के लिए चाहते हैं कि बिना किसी संदर्भ या संदर्भ के विफलता के द्वारा फ़ंक्शन का क्या अर्थ है। सिद्धांत रूप में, मेरे पास असीमित संख्या में फ़ंक्शंस हो सकते हैं जो प्रत्येक विफलता का प्रतिनिधित्व करने वाला एक अलग इंटीजर लौटाता है, संकलक को यह पता होना चाहिए कि एक विशिष्ट इंटीजर विफलता कब होता है? यह केवल संदर्भ के रूप में एक मूल्य है। आप संकलक से आपके प्रोग्राम को लिखने की उम्मीद नहीं कर सकते हैं, अन्यथा हम सभी एग्डा का उपयोग करेंगे। – bheklilr

+0

हाँ, मैं जरूरी नहीं कि यह एक मोनड बनाना चाहता है, लेकिन यह समान होगा। मैं संकलक से यह नहीं जानता कि कौन से पूर्णांक विफल हैं, मैं किसी भी तरह से संकेत देना चाहता हूं कि कौन से पूर्णांक 'f' के लिए विफलता हैं, और 'f2' के लिए एक अलग सेट इंगित करते हैं, और इसी तरह। उदाहरण के लिए, पायथन में मैं टाइपक्लास उदाहरणों के कुछ समकक्षों के लिए एक वैश्विक चर मैपिंग फ़ंक्शन कर सकता हूं। फिर यह संकलन समय के बजाय रनटाइम पर क्रैश होगा यदि उपयोगकर्ता द्वारा उपयोग किए जाने वाले कुछ फ़ंक्शन से कोई मैपिंग नहीं है। लेकिन यह अभी भी सुरक्षित होगा। –

उत्तर

10

ऐसा मत करें। हास्केल की स्थैतिक प्रकार प्रणाली और संदर्भित पारदर्शिता आपको एक बहुत ही उपयोगी गारंटी प्रदान करती है: आप ठीक से सुनिश्चित कर सकते हैं कि कुछ विशेष मान का अर्थ है , चाहे यह कैसे बनाया गया था। इसके साथ हस्तक्षेप करने में असमर्थता नहीं है, न ही गतिशील-शैली “ रनटाइम पुनरावृत्ति ” अभिव्यक्तियों के रूप में, क्योंकि आपको उस कार्य के लिए आवश्यकता होगी जो आपको लगता है।

यदि आपके पास ऐसे कार्य हैं जो तदनुसार इस तरह के विनिर्देश का पालन नहीं करते हैं, तो यह बुरा है। बेहतर से छुटकारा पाएं (कम से कम, उन्हें छुपाएं और केवल एकीकृत व्यवहार के साथ पुनः परिभाषित संस्करण निर्यात करें)। या उपयोगकर्ताओं को बताएं कि उन्हें प्रत्येक के विनिर्देश को देखने के साथ रहना होगा। लेकिन टूटी परिभाषाओं के इस विशेष लक्षण के आसपास कुछ रास्ता हैक करने की कोशिश मत करो।

एक आसान परिवर्तन तुम सिर्फ “ झंडा ” कार्यों के लिए आवेदन कर सकता है जहां विफलता विपरीत यह अन्यथा करता है के रूप में उन्हें इस तरह के एक लिपटे परिणाम लौटने के लिए है का अर्थ है:

newtype Anti a = Anti { profail :: a } 

instance (Anti a) => Fail (Anti a) where 
    isFail (Anti a) = not $ isFail a 

मन: “ एक ही चीज़ ” संभवतः बहुत ही अमूर्त अर्थ में।कोई जरूरत नहीं Left सार्वभौमिक एक “ निर्माता ” असफल होने के लिए नहीं है, यह पर्याप्त है कि यह स्पष्ट है कि यह संस्करण पहले प्रकार तर्क से जुड़े निर्माता, जो नहीं क्या functor/इकाई उदाहरण यह है कि से – पर चल रही है स्वचालित रूप से निम्नानुसार चलता है कि यह “ औसत ” एक monadic अनुप्रयोग में विफलता होगी।
आईई, जब आपने सही प्रकार चुने हैं, तो सामान अनैतिक रूप से बहुत अधिक होना चाहिए; स्पष्ट रूप से विपरीत है जब आप केवल tossing around booleans हैं, तो शायद आपको उनसे पूरी तरह से छुटकारा पाना चाहिए ...

+0

मुझे आपके उत्तर को फिर से पढ़ना होगा, लेकिन, मेरे वास्तविक उपयोग मामले में बूलियन आउटपुट उपयोगी था, क्योंकि कार्यों के बजाय मैं वास्तव में मोनैडिक ऑपरेशंस का उपयोग कर रहा हूं कि वे सफल हुए हैं या नहीं। –

+1

ध्यान दें कि आधार 4.8 'Alt' प्रकार को 'डेटा। मॉनिइड' में जोड़ता है ('वैकल्पिक' को 'मोनॉयड' में बदलना), ताकि यह चुनने का सबसे अच्छा नाम न हो। – dfeuer

+0

@og_loc: मोनैडिक ऑपरेशंस को विफलता सिग्नल करने के लिए बूलियन आउटपुट नहीं करना चाहिए, सामान्य कार्यों से अधिक नहीं होना चाहिए। उत्तरार्द्ध के लिए आप एक उपयुक्त 'शायद' या 'या तो' रैपर का उपयोग करें; पूर्व संबंधित मोनैड ट्रांसफॉर्मर के साथ किया जाना चाहिए। – leftaroundabout

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