2017-01-31 8 views
9

एडवर्ड Kmett के अपवाद लाइब्रेरी ExceptT के लिए MonadMask उदाहरण प्रदान नहीं करता है।ExceptT के लिए कोई MonadMask उदाहरण क्यों नहीं है?

Ben Gamari once asked about this और फिर निष्कर्ष निकाला कि यह दस्तावेज़ीकरण द्वारा समझाया गया था।

नोट इस पैकेज CatchT के लिए एक MonadMask उदाहरण प्रदान करता है कि: यह निकटतम प्रासंगिक दिखने मार्ग मैं मिल सकती है। यह उदाहरण केवल मान्य है यदि आधार मोनड एकाधिक निकास प्रदान करने की कोई क्षमता प्रदान नहीं करता है। उदाहरण के लिए, IO या Either अमान्य आधार मोनैड होंगे, लेकिन Reader या State स्वीकार्य होगा।

लेकिन इसका अर्थ मेरे लिए स्पष्ट नहीं है। "एकाधिक निकास" का क्या अर्थ है और यह MonadMask उदाहरण क्यों प्रतिबंधित करता है?

Michael Snoyman also writes:

[...] 'MonadMask' है, जो आपको लगता है कि कुछ कार्रवाई चलाए जा रहे हैं गारंटी करने के लिए अनुमति देता है, यहां तक ​​कि अपवाद (सिंक्रोनस और एसिंक्रोनस दोनों) की उपस्थिति में। उस गारंटी को प्रदान करने के लिए, मोनैड स्टैक निष्पादन के प्रवाह को नियंत्रित करने में सक्षम होना चाहिए। विशेष रूप से, इसमें उदाहरणों को शामिल नहीं किया गया है [...] एकाधिक निकास बिंदुओं के साथ मोनाड, जैसे ErrorTIO से अधिक।

शायद यह इस विकल्प सवाल पूछने के लिए और अधिक स्पष्ट हो जाएगा: हम अलग ट्रांसफार्मर सेट करते हैं और थोड़ा सरल प्रकार पर विचार करें:

data IOEither a = IOEither { unIOEither :: IO (Either String a) } 
    deriving Functor 

यह लगता है कि हम वास्तव में एक MonadMask उदाहरण लिख सकते हैं :

instance Applicative IOEither where 
    pure = IOEither . return . Right 
    IOEither fIO <*> IOEither xIO = IOEither $ 
     fIO >>= either (return . Left) (\f -> (fmap . fmap) f xIO) 

instance Monad IOEither where 
    IOEither xIO >>= f = IOEither $ 
     xIO >>= either (return . Left) (\x -> unIOEither (f x)) 

instance MonadThrow IOEither where 
    throwM e = IOEither (throwM @IO e) 

instance MonadCatch IOEither where 
    catch (IOEither aIO) f = IOEither $ catch @IO aIO (unIOEither . f) 

instance MonadMask IOEither where 
    mask f = IOEither $ mask @IO $ \restore -> 
     unIOEither $ f (IOEither . restore . unIOEither) 
    uninterruptibleMask f = IOEither $ uninterruptibleMask @IO $ \restore -> 
     unIOEither $ f (IOEither . restore . unIOEither) 

क्या यह उदाहरण मैंने लिखा है ठीक से काम नहीं करता है?

+0

इस सवाल में रुचि भी Snoyman के [* अपवाद हास्केल * में उत्तम आचरण] (https://www.fpcomplete.com/blog/2016/11/exceptions-best-practices-haskell) के लिए विचार करना चाहिए पाठकों 'आईओ (या तो स्ट्रिंग ए)' पर एक राय है कि बस एक प्रकार का उपयोग नहीं किया जाना चाहिए। –

उत्तर

6

नीचे एक ऐसा प्रोग्राम है जो आपके उदाहरणों के साथ समस्या का प्रदर्शन करता है: आप Left के साथ जल्दी से बाहर निकल सकते हैं और इस प्रकार फाइनलर को कभी नहीं चलने का कारण बनता है। यह MonadMask के लिए दस्तावेज़ों में बताए गए कानून के विपरीत है, जिसके लिए gf में क्या होता है, इस पर ध्यान दिए बिना निष्पादित किया जाता है। (या bracket जो कैसे finally कार्यान्वित किया जाता है है) भी इसका अपवाद नहीं finally फेंक दिया जाता है, तो बस >>= का उपयोग करता finalizer चलाने के लिए बाद में लेकिन >>= सही तर्क निष्पादित नहीं करता है, तो बाईं रिटर्न Left: कारण है कि finalizer कभी नहीं चलाया जाता है काफी सरल है।

data IOEither a = IOEither { unIOEither :: IO (Either String a) } 
    deriving Functor 

instance Applicative IOEither where 
    pure = IOEither . return . Right 
    IOEither fIO <*> IOEither xIO = IOEither $ 
     fIO >>= either (return . Left) (\f -> (fmap . fmap) f xIO) 

instance Monad IOEither where 
    IOEither xIO >>= f = IOEither $ 
     xIO >>= either (return . Left) (\x -> unIOEither (f x)) 

instance MonadThrow IOEither where 
    throwM e = IOEither (throwM @IO e) 

instance MonadCatch IOEither where 
    catch (IOEither aIO) f = IOEither $ catch @IO aIO (unIOEither . f) 

instance MonadMask IOEither where 
    mask f = IOEither $ mask @IO $ \restore -> 
     unIOEither $ f (IOEither . restore . unIOEither) 
    uninterruptibleMask f = IOEither $ uninterruptibleMask @IO $ \restore -> 
     unIOEither $ f (IOEither . restore . unIOEither) 

instance MonadIO IOEither where 
    liftIO x = IOEither (Right <$> x) 

main :: IO() 
main = void $ unIOEither $ finally (IOEither (return (Left "exit"))) 
            (liftIO (putStrLn "finalizer")) 
+0

बिल्कुल सही। यही कारण है कि हमने उदाहरण छोड़ दिया। हमने इसे ठीक करने के तरीकों से हमारे दिमाग को तोड़ दिया और यह अच्छा रहा होगा लेकिन यह स्पष्ट अंतिमकरण कानून को पारित नहीं करता है। अंत में हमने अवैध (अगर "स्पष्ट") उदाहरण के खिलाफ या phrasing को समझने के खिलाफ निर्णय लिया जो हमें उपयोगकर्ता त्रुटि के तरीकों से कानूनों को कमजोर करने की अनुमति देगा। –

+0

@ एडवर्ड केएमईटीटी ऐसा लगता है कि हम एक विशेष ब्रैकेट-जैसे ऑपरेशन जैसे 'ब्रैकेट :: आईओ ए -> (ए -> आईओ बी) -> (ए -> एक्सेप्ट टी ई आईओ सी) लिख सकते हैं -> एक्सेप्ट टी ई आईओ सी' क्या हम नहीं कर सके? वह जो 'मोनाडमास्क के कार्यों पर निर्भर नहीं था और सीधे' आईओ 'का' ब्रैकेट 'इस्तेमाल करता था। – danidiaz

+1

@danidiaz: HEAD शाखा देखें। थोड़ी देर पहले https://github.com/ekmett/exceptions/commit/e8cd863ebaf1357091dc7df331bf5c9e73ac21a7 इस नस में कुछ अस्पष्ट रूप से जोड़ा गया। हम वर्तमान में यह देखने के लिए हिला रहे हैं कि हम इसे पसंद करते हैं या नहीं। –

1

monads जो एक गणना से सभी संभव निकास बिंदुओं के लिए खाते हैं, और अतुल्यकालिक अपवाद मुखौटा करने की क्षमता के लिए उपलब्ध कराने के लिए एक वर्ग। निरंतरता आधारित मोनैड, और ErrorT e IO जैसे ढेर जो कई विफलता मोड प्रदान करते हैं, इस वर्ग के अमान्य उदाहरण हैं।

आप IO साथ ErrorT/ExceptT उपयोग करते हैं, "कई निकास बिंदुओं" होने तथ्य को संदर्भित करता है या तो आप एक क्रम अपवाद या एक अपवाद इकाई में फेंक दिया हो सकता है। इनमें से कोई भी गणना समाप्त कर देगा।

runExceptT $ do 
    error "This is an exit point." 
    throwError "This is another exit point." 
    return 23 

यह एक MonadMaskExceptT के लिए है कि पूर्व शर्त के साथ सभी ExceptT e m a के लिए मान्य होगा कि अंतर्निहित इकाई m है आईओ नहीं लिखने के लिए संभव हो जाएगा। इसलिए IO के साथ उपयोग करने के बारे में बड़ी चेतावनी (ऐसा करने से MonadMask उदाहरण अमान्य हो जाता है)।

+0

मेरा 'IOEither' isomorphic' ExceptT स्ट्रिंग IO' नहीं है - जिसका अर्थ है "अंतर्निहित मोनड" * * IO' है? आप 'IOEither' मोनैड में' throwio' या 'वापसी के साथ गणना को समाप्त कर सकते हैं। छोड़ा '। –

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