2012-12-20 16 views
9

मैं जावा अनुप्रयोग को हास्केल पर पोर्ट कर रहा हूं। जावा आवेदन की मुख्य विधि का अनुसरण करता है:हास्केल में प्रारंभिक निकास/वापसी को कैसे कार्यान्वित करें?

public static void main(String [] args) 
{ 
    if (args.length == 0) 
    { 
    System.out.println("Invalid number of arguments."); 

    System.exit(1); 
    } 

    SomeDataType d = getData(arg[0]); 
    if (!dataOk(d)) 
    { 
    System.out.println("Could not read input data."); 

    System.exit(1); 
    } 

    SomeDataType r = processData(d); 
    if (!resultOk(r)) 
    { 
    System.out.println("Processing failed."); 

    System.exit(1); 
    } 

    ... 
} 

तो मैं विभिन्न चरणों है और हर कदम मैं या तो गलत कोड के साथ बाहर निकलें कर सकते हैं, या बाद में निम्नलिखित चरण पर जाएं।

हास्केल को यह पोर्टिंग पर मेरे प्रयास इस प्रकार है:

main :: IO() 
main = do 
     a <- getArgs 
     if (null args) 
      then do 
        putStrLn "Invalid number of arguments." 
        exitWith (ExitFailure 1) 
      else do 
        -- The rest of the main function goes here. 

इस समाधान के साथ, मैं नेस्टेड if-then-else (मूल जावा कोड के प्रत्येक निकास बिंदु के लिए एक) के बहुत सारे होगा।

क्या हास्केल में इस पैटर्न को लागू करने का एक और अधिक सुरुचिपूर्ण/बेवकूफ तरीका है? आम तौर पर, जावा जैसी अनिवार्य भाषा में उपयोग किए जाने वाले प्रारंभिक निकास/वापसी को लागू करने के लिए हास्केल बेवकूफ तरीका क्या है?

+1

कृपया http://learnyouahaskell.com/a-fistful-of-monads पृष्ठ से चलने वाली लाइन का उदाहरण पढ़ें। यह शायद डेटाटाइप का उपयोग करके मोनाड का उदाहरण देता है। एक बार अभिव्यक्ति का परिणाम कुछ भी नहीं है, तो निम्नलिखित सभी अभिव्यक्तियों का नतीजा कुछ भी नहीं है, जैसा कि आप विफलता बिंदु से बाहर निकले हैं। –

+0

@ManojR - शायद यहां वास्तव में उपयुक्त नहीं है क्योंकि आप विफलता का कारण भी चाहते हैं। – Lee

+0

क्लासिक दृष्टिकोण प्रक्रिया को "ट्रस्टिंग" प्रोसेसिंग फ़ंक्शन में विभाजित कर रहा है (जो मानता है कि पैरामीटर सही हैं) और एक "परावर्तक" सैनिटी चेक फ़ंक्शन (जो केवल पैरामीटर सही होने पर जांचता है) ... –

उत्तर

6

एक से थोड़ा अधिक समझदार दृष्टिकोण सशर्त तर्क आप की कोशिश की इसी प्रकार की का उपयोग करता है इस प्रकार दिखाई देंगे:

fallOverAndDie :: String -> IO a 
fallOverAndDie err = do putStrLn err 
         exitWith (ExitFailure 1) 

main :: IO() 
main = do a <- getArgs 
      case a of 
       [d] | dataOk d -> doStuff $ processData d 
        | otherwise -> fallOverAndDie "Could not read input data." 
       _ -> fallOverAndDie "Invalid number of arguments." 


processData r 
    | not (resultOk r) = fallOverAndDie "Processing failed." 
    | otherwise  = do -- and so on... 

इस विशेष मामले में, यह देखते हुए कि exitWith कार्यक्रम है, हम कर सकता है समाप्त हो जाता है वैसे भी भी नेस्टेड सशर्त, पूरी तरह से साथ बांटना:

main :: IO() 
main = do a <- getArgs 
      d <- case a of 
        [x] -> return x 
        _ -> fallOverAndDie "Invalid number of arguments." 
      when (not $ dataOk d) $ fallOverAndDie "Could not read input data." 
      let r = processData d 
      when (not $ resultOk r) $ fallOverAndDie "Processing failed." 

पहले की तरह ही fallOverAndDie का उपयोग करना। यह मूल जावा का एक और अधिक प्रत्यक्ष अनुवाद है।

सामान्य मामले में, MonadEither के लिए उदाहरण आपको शुद्ध कोड में उपर्युक्त उदाहरण के समान कुछ लिखने देता है। इसके बजाय से शुरू:

fallOverAndDie :: String -> Either String a 
fallOverAndDie = Left 

notMain x = do a <- getArgsSomehow x 
       d <- case a of 
         -- etc. etc. 

... शेष कोड मेरे दूसरे उदाहरण से अपरिवर्तित है।आप निश्चित रूप से String के अलावा कुछ और भी उपयोग कर सकते हैं; अधिक विश्वसनीय रूप से IO संस्करण को फिर से बनाने के लिए, आप इसके बजाय Either (String, ExitCode) का उपयोग कर सकते हैं।

साथ ही, Either के इस प्रयोग से निपटने त्रुटि के लिए ही सीमित नहीं है - अगर आप कुछ जटिल गणना एक Double लौटने, जैसा कि ऊपर Either Double Double और एक ही monadic शैली का उपयोग, आप Left उपयोग कर सकते हैं एक वापसी मान वाला जल्दी को उबारने के लिए है , तो दो परिणामों को पतन करने के लिए either id id जैसे कुछ का उपयोग करके फ़ंक्शन को लपेटें और एक Double प्राप्त करें।

3

एक तरीका ErrorT मोनैड ट्रांसफॉर्मर का उपयोग करना है। इसके साथ, आप इसे नियमित मोनैड, रिटर्न, बाइंड, सभी अच्छी चीजों की तरह व्यवहार कर सकते हैं, लेकिन आपको यह फ़ंक्शन भी मिल सकता है, throwError। इससे आप निम्न गणनाओं को तब तक छोड़ सकते हैं जब तक आप monadic गणना के अंत तक नहीं पहुंच जाते, या जब आप catchError को कॉल करते हैं। यह त्रुटि प्रबंधन के लिए है, हालांकि, यह हास्केल में मनमाने ढंग से किसी फ़ंक्शन से बाहर निकलने के लिए नहीं है। मैंने सुझाव दिया क्योंकि ऐसा लगता है कि आप यही कर रहे हैं।

एक त्वरित उदाहरण:

import Control.Monad.Error 
import System.Environment 

data IOErr = InvalidArgs String | GenErr String deriving (Show) 
instance Error IOErr where 
    strMsg = GenErr --Called when fail is called 
    noMsg = GenErr "Error!" 
type IOThrowsError = ErrorT IOErr IO 

process :: IOThrowsError [String] 
process = do 
    a <- liftIO getArgs 
    if length a == 0 
    then throwError $ InvalidArgs "Expected Arguments, received none" 
    else return a 

main = do 
    result <- runErrorT errableCode 
    case result of 
    Right a -> putStrLn $ show a 
    Left e -> putStrLn $ show e 
    where errableCode = do 
    a <- process 
    useArgs a 

अब अगर प्रक्रिया में कोई त्रुटि फेंक दिया, useArgs निष्पादित नहीं किया जाएगा।

0

यह है कि मैं क्या

data ExtendedMaybe a = Just a | GenErr String 

isWrongArgs :: [string] -> ExtendedMaybe [string] 
isWrongArgs p = if (length p == 0) 
then GenErr "Invalid number of arguments" 
else p 

getData :: ExtendedMaybe [string] -> ExtendedMaybe sometype 
getData GenErr = GenErr 
getData [string] = if anything wrong return GenErr "could not read input data" 

processdata :: ExtendedMaybe sometype -> ExtendedMaybe sometype 
processdata GenErr = GenErr 

main = do 
    a <- getArgs 
    d <- isWrongArgs a 
    r <- getData d 
    f <- processdata r 

के साथ आया है मोटे तौर पर विचार आप शायद एक तरह एक डेटाप्रकार है, केवल बजाय कुछ भी नहीं है की आप GenErr स्ट्रिंग है, जो आपको हर कार्य जो प्रक्रिया डेटा में परिभाषित है। यदि इनपुट डेटा प्रकार GenErr बस इसे वापस कर देता है। अन्यथा डेटा में त्रुटि की जांच करें और उचित स्ट्रिंग के साथ GenErr को वापस करें। यह सही तरीका नहीं हो सकता है, लेकिन अभी भी एक तरीका है। यह त्रुटि के सटीक बिंदु से बाहर नहीं निकलता है, लेकिन गारंटी देता है कि त्रुटि होने के बाद बहुत कुछ नहीं हो रहा है। हास्केल में

+8

आपका 'विस्तारित मैबे' प्रकार मूल रूप से 'या तो स्ट्रिंग' जैसा ही है – Lee