6

次の例を見るとcatches

 f = expr `catches` [Handler (\ (ex :: ArithException) -> handleArith ex),
                     Handler (\ (ex :: IOException)    -> handleIO    ex)]

catchesパターン(2つの例外タイプ)に一致するカスタムメカニズムを定義したようです。私は間違っていますか、またはこれを一般化して、特定のパターンに一致するラムダ関数を受け取ることができる関数を定義できるようにすることができますか?

編集:以下の参考文献は、漁獲量のGHCソースです。誰かがこれがどのように機能するかについていくらかの光を当てることができれば、それは素晴らしいことです。

catches :: IO a -> [Handler a] -> IO a
catches io handlers = io `catch` catchesHandler handlers

catchesHandler :: [Handler a] -> SomeException -> IO a
catchesHandler handlers e = foldr tryHandler (throw e) handlers
    where tryHandler (Handler handler) res
              = case fromException e of
                Just e' -> handler e'
                Nothing -> res
4

2 に答える 2

6

これは、機能しているスコープ付き型変数GHC拡張機能です。詳細については、リンクをたどってください。

基本的に、一致する前にパターンが満たす必要のあるタイプに関するアサーションを定義します。だから、ええ、それは警備員に似ていますが、完全にそうではありません。

この特定の例はどのように機能しますか?「ベース」ライブラリのソースを調べて、次のことを確認してください。

class (Show e) => Exception e where
    toException   :: e -> SomeException
    fromException :: SomeException -> Maybe e

data SomeException = forall e . Exception e => SomeException e

instance Exception IOException where
    toException = IOException
    fromException (IOException e) = Just e
    fromException _ = Nothing

instance Exception ArithException where
    toException = ArithException
    fromException (ArithException e) = Just e
    fromException _ = Nothing

これは、型クラスを実装するさまざまな型IOExceptionであることがわかります。また、これは、タイプの値をタイプ、などの値との間で変換できるラッピング/アンラッピングメカニズムであることがわかります。ArithExceptionExceptiontoException/fromExceptionExceptionIOExceptionArithException

したがって、次のように書くことができます。

f = expr `catches` [Handler handleArith,
                    Handler handleIO]

handleArith :: ArithException -> IO ()
handleArith ex = ....

handleIO :: IOException -> IO ()
handleIO ex = ....

それIOExceptionが起こったとしましょう。catchesHandlerハンドラーリストの最初の要素を処理するとき、それはを呼び出しtryHandler、それはを呼び出しますfromException。その定義から、のtryHandler戻り型はのfromException引数と同じである必要がありhandleArithます。一方、eタイプはException、つまり-(IOException ...)です。したがって、タイプはこのように再生されます(これは有効なハスケルではありませんが、私の主張を理解していただければ幸いです):

fromException :: (IOException ...) -> Maybe ArithException

そのinstance Exception IOException ...結果、結果はすぐに続くNothingので、このハンドラーはスキップされます。同じ理由で、次のハンドラーが呼び出されます。これは、fromExceptionを返すため(Just (IOException ...))です。

handleArithそのため、との型アノテーションを使用handleIOして、それぞれがいつ呼び出されるかを指定しfromException/toException、それがこのように行われることを確認しました。

必要に応じて、スコープ付き型変数を使用して、の定義内の型handleIOを制約することもできます。間違いなく、これにより読みやすさが向上する可能性があります。handleArithf

ファイナライズ、スコープ付き型変数はここでは主要なプレーヤーではありません。それらは単に便宜のために使用されます。この種のトリックをプレイするための主な機械はfromException/toException、友達です。スコープ付き型変数を使用すると、ガードパターンにより近い構文を使用できます。

于 2010-02-16T14:07:21.017 に答える
1
case () of 
  ()| foo expr1 -> handleFooCase
    | bar expr2 -> handleBarCase
    | otherwise -> blah
于 2010-02-17T01:39:08.490 に答える