haskell - 如何捕获实例化特定类的所有异常?

标签 haskell ghc

我试图在捕获异常方面实现类似于 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/

相关文章:

haskell - 缩短字符串 Haskell

haskell - Haskell 中的延续与调用堆栈

haskell - "cabal install happy"导致内存溢出。 (GHC 7.8.2)

haskell - Haskell FlexibleInstances 是语言的稳定扩展吗?

haskell - 让重命名函数破坏代码

optimization - Haskell中自动并行的现状

haskell - 结合 StateT 和 State monad

haskell - 带递归的 Monad Transformer

haskell - 以下 haskell 类型类实例有什么问题?

haskell - 酸性状态查询的意外返回类型(Happstack)