2012-12-20 13 views
9

Haskell'e bir Java uygulaması yerleştiriyorum. Java uygulamasının ana yöntem desen aşağıdaki gibidir: Haskell'de erken çıkış/dönüş nasıl uygulanır?

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); 
    } 

    ... 
} 

yüzden farklı adımlar var ve ya bir hata kodu ile ya çıkış can her adımdan sonra aşağıdaki adıma geçin. aşağıdaki gibi Haskell bu taşıma de

girişimim gider: Bu çözüm ile

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. 

, ben iç içe if-then-else (orijinal Java kodunun her çıkış noktası için bir) çok olacaktır.

Bu örüntüyü Haskell'de uygulamak için daha zarif/deyimsel bir yol var mı? Genel olarak, Java gibi bir zorunlu dilde kullanılan bir erken çıkış/geri dönüşü uygulamak için Haskell deyimsel yolu nedir?

+1

Lütfen http://learnyouahaskell.com/a-fistful-of-monads sayfasından Walking the Line örneğini okuyun. Belki detatype kullanarak Monad örneğini verir. Herhangi bir ifadenin sonucu Hiçbir şey olmadığında, aşağıdaki tüm ifadelerin sonucu, başarısızlık noktasında çıktığınız kadar iyi değildir. –

+0

@ManojR - Belki de başarısızlık sebebini de istediğiniz için burada gerçekten uygun değil. – Lee

+0

Klasik yaklaşım, süreci "güvenilir" bir işleme fonksiyonuna (parametrelerin doğru olduğunu varsayar) ve bir "paranoyak" akıl sağlığı kontrol fonksiyonuna (sadece parametrelerin doğru olup olmadığını kontrol eder) bölüyor ... –

cevap

6

Biraz daha mantıklı yaklaşım şöyle olabilir:

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... 

Bu özel durumda, exitWithzaten programı, biz olabilir sonlandırır göz önüne alındığında Ayrıca, tamamen iç içe Koşullamalar vazgeçilebilir:

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." 

önce olduğu gibi aynı fallOverAndDie kullanılması. Bu, orijinal Java'nın çok daha doğrudan bir çevirisi.

Genel durumda, Either için Monad örneği, yukarıdaki son örneğe çok benzer bir şey yazmanıza izin verir. Bundan başlayarak, bu kodun geri kalanı benim ikinci örneğimden değiştirilmez:

.Tabii ki sadece String'dan başka bir şey de kullanabilirsiniz; Daha fazla sadakatle IO sürümünü yeniden oluşturmak için Either (String, ExitCode)'u kullanabilirsiniz.

Ayrıca, Either bu kullanımı işlemeyi hata sınırlı değildir - Biraz karmaşık hesaplama, bir Double dönen yukarıdaki gibi Either Double Double ve aynı monadic stilini kullanarak, bir dönüş değeri ile erken kurtarmak için Left kullanabilirsiniz varsa daha sonra iki sonucu daraltmak ve tek bir Double almak için işlevi either id id gibi bir şey kullanarak sarın.

3

Tek yön ErrorT monad trafosunu kullanmaktır. Bununla birlikte, normal bir monad gibi davranabilir, geri dönebilir, bağlayabilirsiniz, tüm bu güzel şeyler, ama aynı zamanda bu işlevi de alırsınız, throwError. Bu, monadik hesaplamanın sonuna ulaşana kadar veya catchError'u çağırdığınızda aşağıdaki hesaplamaları atlamanıza neden olur. Bu, hata işleme için olsa da, Haskell'de bir işlevden keyfi olarak çıkmak için tasarlanmamıştır. Bunu önerdim çünkü yaptığınız şey bu gibi görünüyor.

Hızlı bir örnek: bir işlemin hataya attı eğer

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 

şimdi, useArgs idam edilmeyeceği. Bu benim

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 

ile geldi ne olduğunu

0

Kabaca bir fikir size hangi işlem verileri her işlevinde tanımlamak GenErr String, tek yerine hiçbir husus, belki bir benzeri bir veri türü olması. Girdi veri türü GenErr ise bunu geri döndürün. Aksi halde verilerdeki hatayı kontrol edin ve GenErr'i uygun dizeyle döndürün. Bu mükemmel bir yol olmayabilir, ama yine de tek yol. Bu, tam hata noktasında çıkmaz, ancak hata meydana geldikten sonra çok fazla şeyin gerçekleşmediğini garanti eder. Eğer çalıştı koşullu mantık aynı tür kullanan Haskell

+8

'ExtendedMaybe' türünüz temelde 'Dizgenin' ile aynıdır – Lee