2012-12-20 18 views
9

私はJavaアプリケーションをHaskellに移植しています。 Javaアプリケーションのmainメソッドは、パターンに従う:Haskellで早期のexit/returnを実装する方法は?

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

    ... 
} 

だから、私は異なるステップと、各ステップは、Iのいずれかできるエラーコードで終了するか、次のステップに進み後有します。次のようにHaskellのにこれを移植で

私の試みが行く:

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. 

このソリューションでは、私は(元のJavaコードの各出口点に1つ)、ネストされたif-then-elseがたくさんあります。

ハスケルでこのパターンを実装するためのよりエレガントで慣用的な方法はありますか?一般的に、Javaのような命令的言語で使われている初期のexit/returnを実装するHaskellの慣用方法は何ですか?

+1

http://learnyouahaskell.com/a-fistful-of-monadsページからWalking the Lineの例をお読みください。これは、おそらくデータ型を使ってMonadの例を示しています。式の結果がNothingになると、すべての次の式の結果は、Nothingになります。これは、失敗した時点で終了したものと同じです。 –

+0

@ManojR - あなたは失敗の理由も欲しいので、おそらくここにはあまり適していないでしょう。 – Lee

+0

古典的なアプローチでは、プロセスを「信頼する」処理機能(パラメータが正しいと仮定)と「パラノイド」健全性チェック機能(パラメータが正しいかどうかのみをチェックする)に分割しています... –

答えて

6

Aもう少し賢明なアプローチは、次のようになります。この特定のケースで

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を使用します。これは元のJavaのはるかに直接的な翻訳です。

MonadEitherのインスタンスでは、上記の後半の例と非常によく似たものを純粋なコードで記述できます。代わりにこれから始める:

...残りのコードは、私の2番目の例と同じです。もちろんString以外のものを使用することもできます。 IOバージョンをより忠実に再現するには、代わりにEither (String, ExitCode)を使用できます。

また、Eitherのこの使用は、エラー処理に限定されるものではない - あなたは、上記のようEither Double Doubleと同じモナドスタイルを使用して、Doubleを返すいくつかの複雑な計算を持っている場合は、戻り値を早期に救済するためにLeftを使用することができますeither id idのようなものを使って関数をラップし、2つの結果を崩壊させ、単一のDoubleを得る。

3

ErrorTモナドトランスを使用する方法もあります。それで、あなたは普通のモナドのように扱うことができます、戻り、バインド、すべての良いものが、あなたはまた、この関数、throwErrorを取得します。これにより、モナド計算の終わりに達するか、catchErrorを呼び出すまで次の計算をスキップできます。これはエラー処理のためのものですが、Haskellで任意に関数を終了させるためのものではありません。私はそれがあなたのやっているように思えるので、それを提案しました。

簡単な例:プロセスがエラーを投げた場合、今、useArgsが実行されないでしょう

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 

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を適切な文字列で返します。これは完璧な方法ではないかもしれませんが、まだ片方の方法です。これはエラーの正確な時点で終了しませんが、エラーが発生した後ではあまり起こらないことを保証します。あなたがしようとした条件ロジックの同じ種類を使用していますHaskellでは

+8

'ExtendedMaybe'タイプは基本的に' Either String'と同じです – Lee

関連する問題