हास्केल में अपवाद हैंडलिंग


79

मुझे तीन हास्केल कार्यों के उपयोग को समझने में मदद चाहिए

  • कोशिश करो ( Control.Exception.try :: Exception e => IO a -> IO (Either e a))
  • पकड़ना ( Control.Exception.catch :: Exception e => IO a -> (e -> IO a) -> IO a)
  • संभाल ( Control.Exception.handle :: Exception e => (e -> IO a) -> IO a -> IO a)

मुझे कई चीजें जानने की जरूरत है:

  1. मैं किस फ़ंक्शन का उपयोग करता हूं?
  2. मैं कुछ सरल उदाहरण के साथ इस फ़ंक्शन का उपयोग कैसे करूं?
  3. पकड़ और संभाल के बीच अंतर कहां है? उनके पास लगभग एक ही हस्ताक्षर है एक अलग आदेश के साथ।

मैं अपने परीक्षणों को लिखने की कोशिश करूंगा और आशा करता हूं कि आप मेरी मदद कर सकते हैं:

प्रयत्न

मेरे पास एक उदाहरण है:

x = 5 `div` 0
test = try (print x) :: IO (Either SomeException ())

मेरे दो सवाल हैं:

  1. मैं एक कस्टम त्रुटि आउटपुट कैसे सेट कर सकता हूं?

  2. मैं SomeException को सभी त्रुटियों को सेट करने के लिए क्या कर सकता हूं इसलिए मुझे लिखना नहीं चाहिए :: IO (Either SomeException())

पकड़ना / आज़माना

क्या आप मुझे कस्टम त्रुटि आउटपुट के साथ एक छोटा उदाहरण दिखा सकते हैं?


जवाबों:


132

मैं किस फ़ंक्शन का उपयोग करता हूं?

यहाँ Control.Exception प्रलेखन से सिफारिश की गई है:

  • यदि आप इस घटना में कुछ सफाई करना चाहते हैं कि एक अपवाद उठाया, उपयोग finally, bracketया है onException
  • एक अपवाद के बाद ठीक होने और कुछ और करने के लिए, tryपरिवार में से किसी एक का उपयोग करने के लिए सबसे अच्छा विकल्प है ।
  • ... जब तक आप एक अतुल्यकालिक अपवाद से पुनर्प्राप्त नहीं कर रहे हैं, जिस स्थिति में उपयोग करें catchया catchJust

प्रयास करें :: अपवाद e => IO a -> IO (Either ea)

tryIOचलाने के लिए एक कार्रवाई करता है, और एक रिटर्न देता है Either। यदि गणना सफल हुई, तो परिणाम एक Rightकंस्ट्रक्टर में लिपटे हुए है । (गलत के विपरीत सही समझें)। यदि क्रिया निर्दिष्ट प्रकार के अपवाद को फेंक देती है , तो उसे एक Leftनिर्माता में वापस कर दिया जाता है । यदि अपवाद उपयुक्त प्रकार का नहीं था , तो यह स्टैक का प्रचार करना जारी रखता है। SomeExceptionप्रकार के रूप में निर्दिष्ट करना सभी अपवादों को पकड़ लेगा, जो एक अच्छा विचार हो सकता है या नहीं भी हो सकता है।

ध्यान दें कि यदि आप एक शुद्ध संगणना से एक अपवाद को पकड़ना चाहते हैं, तो आपको evaluateमूल्यांकन को बल देने के लिए उपयोग करना होगा try

main = do
    result <- try (evaluate (5 `div` 0)) :: IO (Either SomeException Int)
    case result of
        Left ex  -> putStrLn $ "Caught exception: " ++ show ex
        Right val -> putStrLn $ "The answer was: " ++ show val

पकड़ :: अपवाद e => IO a -> (e -> IO a) -> IO a

catchके समान है try। यह पहले निर्दिष्ट IOकार्रवाई को चलाने की कोशिश करता है , लेकिन अगर एक अपवाद को फेंक दिया जाता है तो हैंडलर को एक वैकल्पिक उत्तर प्राप्त करने के लिए अपवाद दिया जाता है।

main = catch (print $ 5 `div` 0) handler
  where
    handler :: SomeException -> IO ()
    handler ex = putStrLn $ "Caught exception: " ++ show ex

हालांकि, एक महत्वपूर्ण अंतर है। catchअपने हैंडलर का उपयोग करते समय एक एसिंक्रोनस अपवाद द्वारा बाधित नहीं किया जा सकता है (यानी किसी अन्य थ्रेड के माध्यम से फेंक दिया जाता है throwTo)। जब तक आपके हैंडलर ने दौड़ना समाप्त नहीं किया है, तब तक एक विषम अपवाद को उठाने का प्रयास अवरुद्ध होगा।

ध्यान दें कि catchप्रस्तावना में एक अलग है, इसलिए आप करना चाह सकते हैं import Prelude hiding (catch)

संभाल :: अपवाद e => (e -> IO a) -> IO a -> IO a

handleबस catchउलटे क्रम में तर्कों के साथ है। कौन सा उपयोग करना है यह इस बात पर निर्भर करता है कि आपके कोड को अधिक पठनीय बनाता है, या यदि आप आंशिक अनुप्रयोग का उपयोग करना चाहते हैं तो कौन बेहतर है। वे अन्यथा समान हैं।

tryJust, catchJust और handleJust

ध्यान दें कि try, catchऔर निर्दिष्ट / अनुमानित प्रकार के सभी अपवादों handleको पकड़ लेगा । और मित्र आपको एक चयनकर्ता फ़ंक्शन निर्दिष्ट करने की अनुमति देते हैं जो फ़िल्टर करता है जो विशेष रूप से आप संभालना चाहते हैं। उदाहरण के लिए, सभी अंकगणितीय त्रुटियाँ प्रकार की हैं । यदि आप केवल पकड़ना चाहते हैं , तो आप कर सकते हैं:tryJustArithExceptionDivideByZero

main = do
    result <- tryJust selectDivByZero (evaluate $ 5 `div` 0)
    case result of
        Left what -> putStrLn $ "Division by " ++ what
        Right val -> putStrLn $ "The answer was: " ++ show val
  where
    selectDivByZero :: ArithException -> Maybe String
    selectDivByZero DivideByZero = Just "zero"
    selectDivByZero _ = Nothing

शुद्धता पर ध्यान दें

ध्यान दें कि इस प्रकार का अपवाद हैंडलिंग केवल अशुद्ध कोड (यानी IOमोनाड) में हो सकता है। यदि आपको शुद्ध कोड में त्रुटियों को संभालने की आवश्यकता है, तो आपको Maybeया Either(या कुछ अन्य बीजीय डेटाटाइप) का उपयोग करके लौटने वाले मूल्यों पर ध्यान देना चाहिए । यह अक्सर बेहतर होता है क्योंकि यह अधिक स्पष्ट होता है इसलिए आप हमेशा जानते हैं कि क्या हो सकता है। Control.Monad.Errorइस तरह के त्रुटि के साथ काम करने के लिए मोनाड इस तरह की त्रुटि को आसान बनाते हैं।


यह सभी देखें:


8
काफी जानकारीपूर्ण, लेकिन मुझे आश्चर्य है कि आपने Control.Exception डॉक्स से अंगूठे के नियम को छोड़ दिया। Ie "का उपयोग करें try, जब तक कि आप एक अतुल्यकालिक अपवाद से उबर नहीं रहे हैं, जिस स्थिति में उपयोग करें catch"
जॉन एल


2

मैं देख रहा हूं कि एक बात जो आपको (आपके दूसरे प्रश्न) को :: IO (Either SomeException ())परेशान करती है, वह है लेखन और इससे मुझे भी गुस्सा आ रहा है।

मैंने अब इससे कुछ कोड बदल दिए हैं:

let x = 5 `div` 0
result <- try (print x) :: IO (Either SomeException ())
case result of
    Left _ -> putStrLn "Error"
    Right () -> putStrLn "OK"

इसके लिए:

let x = 5 `div` 0
result <- try (print x)
case result of
    Left (_ :: SomeException) -> putStrLn "Error"
    Right () -> putStrLn "OK"

ऐसा करने के लिए, आपको ScopedTypeVariablesजीएचसी विस्तार का उपयोग करना होगा लेकिन मुझे लगता है कि सौंदर्यशास्त्र यह इसके लायक है।


1

पुन: प्रश्न 3: पकड़ और संभाल समान हैं ( हुगले के माध्यम से पाया जाता है )। उपयोग करने का विकल्प आमतौर पर प्रत्येक तर्क की लंबाई पर निर्भर करेगा। यदि क्रिया कम है, तो कैच का उपयोग करें और इसके विपरीत। प्रलेखन से सरल संभाल उदाहरण:

do handle (\NonTermination -> exitWith (ExitFailure 1)) $ ...

साथ ही, आप कस्टम हैंडलर बनाने के लिए हैंडल फ़ंक्शन को कड़ाई से कर सकते हैं, जिसे आप बाद में पास कर सकते हैं, जैसे। (प्रलेखन से अनुकूलित):

let handler = handle (\NonTermination -> exitWith (ExitFailure 1))

कस्टम त्रुटि संदेश:

do       
    let result = 5 `div` 0
    let handler = (\_ -> print "Error") :: IOException -> IO ()
    catch (print result) handler
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.