Haskell中抛出异常的小例子

js81xvg6  于 2023-10-19  发布在  其他
关注(0)|答案(1)|浏览(111)

我在Haskell中有一个函数,它通过迭代工作,每当达到3000次迭代时就会抛出错误。
最近我发现了Control.Exception,它允许定义、抛出和捕获异常。我想多了解一些。
我觉得这很难理解(对于像我这样的Haskell新手来说)。所以我需要一些帮助。
当达到3000次迭代时,我想抛出我的自定义异常,并显示类似“Exception:3000次迭代”。我在文档中找到了displayException,这是要走的路吗?
到目前为止,这是我所做的(这仍然是一个测试阶段):

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ExistentialQuantification #-}

import Control.Exception (Exception)

-- for testing purpose
data MyException = ThisException | ThatException
  deriving Show

instance Exception MyException 

data ReachedMax = forall e.Exception e => ReachedMax e

instance Show ReachedMax where
    showsPrec :: Int -> ReachedMax -> ShowS
    showsPrec p (ReachedMax e) = showsPrec p e

有时(经常)我应该说我盲目地复制HLS的建议。我走对了吗?我可以继续使用ReachedMax构造函数吗?

后台

正如你们中的一些人所知道的,这些天我在用Haskell做2D图像。其中一个依赖于具有此功能的包,该功能可实现3000次迭代。我想抓住这些幽灵。

smdnsysy

smdnsysy1#

当GHC运行时捕获异常并通过消息停止程序时:

Exception: blah blah blah

"blah blah blah"字符串由showShow类型类生成。(为此,Exception中的displayException方法将被忽略。
所以,你需要定义一个自定义的异常类型:

data ReachedMax = ReachedMax Int

给予一个Show示例,用于显示所需的错误消息:

instance Show ReachedMax where
  show (ReachedMax n) = show n ++ " iterations"

并使其成为Exception类的示例:

instance Exception ReachedMax

然后,当你在纯代码中throw这个异常时,当相关的表达式被求值时,程序将被你的自定义异常终止。
例如,如果你有一个函数测试Collatz猜想的整数,并返回所需的迭代次数(但如果迭代次数超过100,则抛出自定义异常):

collatz :: Int -> Int
collatz n0 = go 0 n0
  where go iter 1 = iter
        go iter n | iter <= 100 = go (iter+1) (step n)
                  | otherwise   = throw $ ReachedMax 100
        step n | even n    = n `div` 2
               | otherwise = 3*n + 1

在GHCi中运行几个测试,你应该看到所需的行为:

λ> collatz 1
0
λ> collatz 10
6
λ> collatz 27
*** Exception: 100 iterations
λ>

如果你想捕获这个异常,你需要在IO monad中强制计算纯代码,并使用catchtry来捕获所需的异常:

main :: IO ()
main = do

  -- example using `try`
  r1 <- try @ReachedMax $ evaluate (collatz 27)
  print r1

  -- example using `catch`

  r2 <- evaluate (collatz 27) `catch` (\(_ :: ReachedMax) -> pure (-999))
  print r2

在这两种情况下,捕获函数都需要用正确的Exception类型示例化,并且该类型确定捕获哪些异常。
你只需要以下形式的existential exceptions:

data SomeSortOfException = forall e. Exception e => SomeSortOfException

如果要创建异常的层次结构,以便可以捕获包含多个异常类型的层次结构的分支,而不是捕获特定的异常类型,请使用。
完整代码示例:

module Collatz where

import Control.Exception

data ReachedMax = ReachedMax Int
instance Show ReachedMax where
  show (ReachedMax n) = show n ++ " iterations"
instance Exception ReachedMax

collatz :: Int -> Int
collatz n0 = go 0 n0
  where go iter 1 = iter
        go iter n | iter <= 100 = go (iter+1) (step n)
                  | otherwise   = throw $ ReachedMax 100
        step n | even n    = n `div` 2
               | otherwise = 3*n + 1

main :: IO ()
main = do

  -- example using `try`
  r1 <- try @ReachedMax $ evaluate (collatz 27)
  print r1

  -- example using `catch`

  r2 <- evaluate (collatz 27) `catch` (\(_ :: ReachedMax) -> pure (-999))
  print r2

相关问题