我试图在捕获异常方面实现类似于 Java/C# 的表现力,我可以在其中指定我想要捕获的异常的接口(interface),否则我需要枚举所有可能的类型。
interface I {void f();}
class AE extends Exception implements I {}
class BE extends Exception implements I {}
try {
throw (new Random().next() % 2 == 0
? new AE()
: new BE());
} catch (I e) {
e.f();
}
class I e where f :: e -> IO ()
data AE = AE deriving (Show)
data BE = BE deriving (Show)
instance Exception AE
instance Exception BE
instance I AE where f _ = putStrLn "f AE"
instance I BE where f _ = putStrLn "f BE"
run m = try @(forall e . (I e, Exception e) => e) m >>= \case
Left er -> f er
Right () -> pure ()
编译器提示:
GHC doesn't yet support impredicative polymorphism
原始错误是由 ghc 8.10.7 产生的。
GHC 9.2.1 已发布。 启用 ImpredcitveTypes 后,错误有所不同:
• No instance for (Exception (forall e. I e => e))
arising from a use of ‘try’
最佳答案
我将再次取消删除此答案,但请阅读 Fyodor Solkin's首先。
正如我已经评论过的,这可能完全是错误的方法,您应该使用单一的异常类型
data I = AE | BE ...
但是,如果您坚持拥有开放类型类功能,则此类型也可以是存在的(基本上,这就是 OO 对基类的引用)。请注意 existentials should not be used除非你真的有充分的理由这样做。
{-# LANGUAGE GADTs, LambdaCase, StandaloneDeriving #-}
import Control.Exception
class I e where f :: e -> IO ()
data AE = AE deriving (Show)
instance I AE where f _ = putStrLn "f AE"
data BE = BE deriving (Show)
instance I BE where f _ = putStrLn "f BE"
data AnI where
AnI :: (I e, Show e) => e -> AnI
deriving instance Show AnI
instance Exception AnI
run m = try m >>= \case
Left (AnI er) -> f er
Right () -> pure ()
main :: IO ()
main = run (throw (AnI BE))
exception-via库似乎解决了明确包装这些存在的尴尬,并允许创建异常的实际层次结构。我还没有尝试过这个库,但它看起来很有希望。
另一种方法是
{-# LANGUAGE DataKinds, KindSignatures, ScopedTypeVariables, UnicodeSyntax
, MultiParamTypeClasses, FlexibleInstances, ConstraintKinds
, AllowAmbiguousTypes
, TypeApplications, RankNTypes, DeriveAnyClass, TypeOperators #-}
import Control.Exception
import Data.Kind
class PolyExcept (c :: Type -> Constraint) (l :: [Type]) where
handleAll :: (∀ e . c e => e -> IO a) -> IO a -> IO a
instance PolyExcept c '[] where
handleAll _ = id
instance ∀ c e l . (Exception e, c e, PolyExcept c l)
=> PolyExcept c (e ': l) where
handleAll h a = handle @e h (handleAll @c @l h a)
class I e where f :: e -> IO ()
data AE = AE deriving (Show, Exception)
instance I AE where f _ = putStrLn "f AE"
data BE = BE deriving (Show, Exception)
instance I BE where f _ = putStrLn "f BE"
run :: IO () -> IO ()
run = handleAll @I @'[AE, BE] f
main :: IO ()
main = run (throw BE)
关于haskell - 如何捕获实例化特定类的所有异常?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69779254/