haskell यह समझना कि फनकार का एक उदाहरण है




typeclass functor (4)

अपने खाली समय में मैं हास्केल सीख रहा हूं, इसलिए यह एक शुरुआती सवाल है।

मेरी रीडिंग में मुझे एक उदाहरण सामने आया है जिसमें बताया गया है कि किस तरह से Functor का एक उदाहरण बनाया गया है:

instance Functor (Either a) where
    fmap f (Right x) = Right (f x)
    fmap f (Left x) = Left x

अब, मैं यह समझने की कोशिश कर रहा हूं कि Right वैल्यू कंस्ट्रक्टर के मामले में कार्यान्वयन मैप क्यों होता है, लेकिन Left के मामले में नहीं है?

यहाँ मेरी समझ है:

पहले मुझे उपरोक्त उदाहरण के रूप में फिर से लिखना

instance Functor (Either a) where
    fmap g (Right x) = Right (g x)
    fmap g (Left x) = Left x

अभी व:

  1. मुझे पता है कि fmap :: (c -> d) -> fc -> fd

  2. अगर हम Either a साथ f विकल्प देते हैं तो हमें fmap :: (c -> d) -> Either ac -> Either ad

  3. Right (gx) का प्रकार Either a (gx) , और gx का प्रकार d , इसलिए हमारे पास है कि Right (gx) का प्रकार Either ad , जिसे हम fmap से उम्मीद fmap (देखें 2. ऊपर)

  4. अब, यदि हम Left (gx) देखते हैं, तो हम यह कहने के लिए एक ही तर्क का उपयोग कर सकते हैं कि इसका प्रकार Either (gx) b , Either db , जो हम fmap से उम्मीद नहीं fmap (देखें 2. ऊपर): d दूसरा पैरामीटर होना चाहिए, पहला नहीं! इसलिए हम Left नक्शा नहीं बना सकते।

क्या मेरा तर्क सही है?


आपका खाता सही है। हो सकता है कि इस तरह के उदाहरणों के कारण हमें कठिनाई हो रही है कि हम वास्तव में एक ही बार में कई मज़ेदार उदाहरणों को परिभाषित कर रहे हैं - प्रत्येक संभावित Left प्रकार के लिए। लेकिन एक फनकार उदाहरण सिस्टम में असीम रूप से कई प्रकारों पर काम करने का एक व्यवस्थित तरीका है। इसलिए हम असीम रूप से सिस्टम में असीम रूप से कई प्रकारों के ऑपरेटिंग तरीके को परिभाषित कर रहे हैं। उदाहरण में दो तरीकों से सामान्यता शामिल है।

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

data MightBe b     = Nope ()    | Yep b
data UnlessError b = Bad String | Good b
data ElseInt b     = Else Int   | Value b

यहाँ हम थक सकते हैं और एक अमूर्त बना सकते हैं:

data Unless a b    = Mere a     | Genuine b

अब हम अपने फनकार के उदाहरणों को, अनपेक्षित रूप से, पहले बहुत कुछ देख रहे हैं जैसे कि Maybe उदाहरण के लिए:

instance Functor MightBe where
  fmap f (Nope ()) = Nope ()   -- compare with Nothing
  fmap f (Yep x)   = Yep (f x) -- compare with Just (f x)

instance Functor UnlessError where
  fmap f (Bad str) = Bad str   -- a more informative Nothing
  fmap f (Good x)  = Good (f x)

instance Functor ElseInt where
  fmap f (Else n) = Else n 
  fmap f (Value b) = Value (f b)

लेकिन, फिर से, क्यों परेशान करते हैं, चलो अमूर्त बनाते हैं:

instance Functor (Unless a) where
  fmap f (Mere a) = Mere a
  fmap f (Genuine x) = Genuine (f x)

Mere a शब्दों को छुआ नहीं गया है, क्योंकि () , String और Int मान को छुआ नहीं गया था।


यह सही है। इस व्यवहार का एक अन्य महत्वपूर्ण कारण यह भी है: आप Either ab को अभिकलन के रूप में सोच सकते हैं, जो सफल हो सकता है और b या त्रुटि संदेश के साथ विफल हो सकता a । (यह भी है, कैसे मोनड उदाहरण काम करता है)। इसलिए यह केवल स्वाभाविक है, कि फ़नकार का उदाहरण Left मूल्यों को नहीं छूएगा, क्योंकि आप अभिकलन पर नक्शा बनाना चाहते हैं, यदि यह विफल रहता है, तो हेरफेर करने के लिए कुछ भी नहीं है।


अब, मैं यह समझने की कोशिश कर रहा हूं कि राइट वैल्यू कंस्ट्रक्टर के मामले में कार्यान्वयन मैप क्यों होता है, लेकिन लेफ्ट के मामले में नहीं है?

यहाँ प्लग करें और यह समझ में आ सकता है।

मान लें = स्ट्रिंग (एक त्रुटि संदेश) आप या तो एक फ़्लोट में लागू होते हैं।

तो आपके पास एक एफ: फ्लोट -> इंटेगर उदाहरण के लिए राउंडऑफ कहते हैं।

(या तो स्ट्रिंग) (फ्लोट) = या तो स्ट्रिंग फ्लोट।

अब (fmap f) :: या तो स्ट्रिंग फ्लोट -> या तो स्ट्रिंग इंट तो आप एफ के साथ क्या करने जा रहे हैं? f के पास कोई सुराग नहीं है कि आप स्ट्रिंग्स के साथ क्या करें ताकि आप वहां कुछ भी न कर सकें। यह स्पष्ट रूप से केवल एक चीज है जिस पर आप कार्य कर सकते हैं, जबकि बाएं मूल्यों को अपरिवर्तित रखते हुए सही मान हैं।

दूसरे शब्दों में, या तो एक फ़नकार है क्योंकि इस तरह का एक स्पष्ट फमा है:

  • सही मानों के लिए एफ लागू करें
  • वामपंथी मूल्यों के लिए कुछ नहीं करते

जैसा कि दूसरों ने उल्लेख किया है, दोनों ही तर्कों में Either टाइप एक फनकार है। लेकिन हास्केल में हम एक प्रकार के अंतिम तर्कों में केवल (सीधे) परिभाषित कर सकते हैं। इस तरह के मामलों में, हम newtype s का उपयोग करके सीमा के आसपास प्राप्त कर सकते हैं:

newtype FlipEither b a = FlipEither { unFlipEither :: Either a b }

तो हमारे पास कन्स्ट्रक्टर FlipEither :: Either ab -> FlipEither ba जो कि हमारे newtype में स्वैप टाइप आर्ग्यूमेंट्स के साथ Either रैप करता है। और हमारे पास dectructor unFlipEither :: FlipEither ba -> Either ab जो इसे वापस unFlipEither :: FlipEither ba -> Either ab देता है। अब हम FlipEither के अंतिम तर्क में एक FlipEither उदाहरण को परिभाषित कर सकते हैं, जो वास्तव में Either पहला तर्क है:

instance Functor (FlipEither b) where
    fmap f (FlipEither (Left x))  = FlipEither (Left (f x))
    fmap f (FlipEither (Right x)) = FlipEither (Right x)

ध्यान दें कि अगर हम कुछ समय के लिए FlipEither को भूल जाते हैं, तो हमें Left / Right स्वैप के साथ सिर्फ Either FlipEither की परिभाषा मिलती है। और अब, जब भी हमें Either पहले प्रकार के तर्क में एक Functor इंस्टेंस की आवश्यकता होती है, तो हम मूल्य को FlipEither में लपेट सकते हैं और बाद में इसे खोल सकते हैं। उदाहरण के लिए:

fmapE2 :: (a -> b) -> Either a c -> Either b c
fmapE2 f = unFlipEither . fmap f . FlipEither

अद्यतन: Data.Bifunctor , जिनमें से Either (,) उदाहरण हैं पर एक नज़र है। प्रत्येक bifunctor में दो तर्क हैं और उनमें से प्रत्येक में एक bifunctor है। यह first और second में Bifunctor की विधियों में परिलक्षित होता है।

Either Bifunctor की परिभाषा बहुत ही सममित है:

instance Bifunctor Either where
    bimap f _ (Left a)  = Left (f a)
    bimap _ g (Right b) = Right (g b)

    first  f = bimap f id

    second f = bimap id f






either