Haskell でカスタム ガード メカニズムを定義できますか?
-
20-09-2019 - |
質問
の例を見ると、 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