質問

の例を見ると、 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
役に立ちましたか?

解決

これは スコープ付き型変数 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 そして ArithException 型クラスを実装する異なる型である Exception. 。私たちもそれを見ています toException/fromException 型の値を変換できるようにするラップ/ラップ解除メカニズムです。 Exception 型の値との間の値 IOException, ArithException, 、など。

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

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 例外タイプ、つまり - (IOException ...) です。したがって、型は次のように展開されます (これは有効な Haskell ではありませんが、私の言いたいことを理解していただければ幸いです)。

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

から instance Exception IOException ... 結果は次のようになります。 Nothing, そのため、このハンドラーはスキップされます。同じ理由で、次のハンドラーが呼び出されます。 fromException 戻るだろう (Just (IOException ...)).

つまり、次の型シグネチャを使用しました handleArith そして handleIO それぞれがいつ呼び出されるかを指定し、 fromException/toException それがこのように起こったことを確認しました。

必要に応じて、次のタイプを制限することもできます。 handleIO そして handleArith の定義の中で f, 、スコープ型変数を使用します。おそらく、これにより読みやすさが向上する可能性があります。

ファイナライズ中のスコープ付き型変数は、ここでは主要な役割を果たしません。それらは便宜的に使用されているだけです。この種のトリックをプレイするための主なマシンは次のとおりです。 fromException/toException と友達。スコープ付き型変数を使用すると、ガード パターンによりよく似た構文を使用できるようになります。

他のヒント

case () of 
  ()| foo expr1 -> handleFooCase
    | bar expr2 -> handleBarCase
    | otherwise -> blah
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top