1

ファイルが存在する場合はファイルを安全に読み取り、ファイルが存在しない場合は何もしない単純な関数を作成しようとしています。

safeRead :: String -> IO ()
safeRead path = readFile path `catch` handleExists
  where handleExists e
          | isDoesNotExistError e = return ()
          | otherwise = throwIO e

これはコンパイル時に失敗します

Couldn't match type ‘[Char]’ with ‘()’
Expected type: IO ()
  Actual type: IO String
In the first argument of ‘catch’, namely ‘readFile path’
In the expression: readFile path `catch` handleExists

であるため、これは理にかなって:t readFileいますreadFile :: FilePath -> IO String。を返す関数IO Stringなど (とIO String同じではありませんIO ())

署名の変更String -> IO String

Couldn't match type ‘()’ with ‘[Char]’
Expected type: IOError -> IO String
  Actual type: IOError -> IO ()
In the second argument of ‘catch’, namely ‘handleExists’
In the expression: readFile path `catch` handleExists

handleExists には型があるので、これも理にかなっていますIO ()

全員のルックアップを保存するには、catch を次のようにインポートします。catch import Control.Exception のシグネチャは次のとおりです。 catch :: Exception e => IO a -> (e -> IO a) -> IO a

私の本当の質問は、この種のエラーセーフで柔軟なコードを Haskell で書くにはどうすればよいかということです。より具体的には、成功ケースと失敗ケースの両方を処理できるようにするには、この関数にどのような変更を加える必要があるでしょうか?

4

1 に答える 1

8

関数に実際に何をさせたいのかを理解する必要があります。

ファイルの読み取りに成功した場合は、内容を文字列として返す必要があります。

失敗した場合、実際に何をしたいですか?空の文字列またはその他のフォールバック コンテンツを返しますか? 次に、 の最初のケースで を にreturn ()変更するだけです。return ""handleExists

ただし、戻り値の型でエラーを示したい場合は、 とは異なる型を返す必要がありますString。Carsten が言ったように、成功とエラーに対して aMaybe Stringと gを返すことができます。または、代わりにエラー メッセージが必要な場合は を返すこともできます。Just theStringNothingEither

ファイルが存在しないことのみをキャッチし、他のエラーを再スローするため、この特定の機能についてMaybe Stringは、最も理にかなっていると思います。次に、コードは次のようになります。

safeRead :: String -> IO (Maybe String)
safeRead path = (fmap Just $ readFile path) `catch` handleExists
  where
    handleExists :: IOException -> IO (Maybe String)
    handleExists e
      | isDoesNotExistError e = return Nothing
      | otherwise = throwIO e

ここでは、型要件を満たすためreadFileに aの結果をラップし、エラーの場合は unit の代わりに return を返します。JustNothing

于 2016-01-08T10:11:47.683 に答える