Haskell:如果发生异常,则不返回任何内容

nvr*_*dow 1 haskell try-catch maybe

{-# LANGUAGE DeriveDataTypeable, ScopedTypeVariables #-}

import Data.Typeable
import Control.Exception

data EmptyListException = EmptyListException
    deriving (Show, Typeable)
instance Exception EmptyListException

myHead :: [a] -> a
myHead []    = throw EmptyListException
myHead (x:_) = x

mySafeHead :: [a] -> IO (Maybe a)
mySafeHead xs = (return (Just (myHead xs)))
                `catch`
                (\(ex::EmptyListException) -> return Nothing)
Run Code Online (Sandbox Code Playgroud)

我想返回xs的第一个元素,如果有的话.否则我想返回"Nothing",但它返回包含在"Just"中的Exception.这是为什么?PS:我必须使用myHeadmySaveHead.

ram*_*ion 7

所以当我运行你的代码时,这就是我所看到的

? mySafeHead []
Just *** Exception: EmptyListException
Run Code Online (Sandbox Code Playgroud)

我明白为什么你会把它描述为"它返回包含在"Just"中的Exception.",但这实际上不是正在发生的事情.

Haskell是非严格的,因此它推迟计算直到需要一个值.

在未检查mySafeHead的值中myHead xs,因此不进行评估.相反,该值的计算保留为thunk,它被包装Just并返回.

然后,在ghci尝试打印该值时,最终强制该thunk,并引发异常.由于我们现在已经超出了catch语句的范围,因此它不适用,并且异常使它一直到终端,它终止输出的打印.

解决这个问题的简单方法是在退出语句之前使用seq强制评估:myHead xscatch

mySafeHead' :: [a] -> IO (Maybe a)                             
mySafeHead' xs = (let x = myHead xs in x `seq` return (Just x))
                `catch`                                        
                (\(_::EmptyListException) -> return Nothing)   
Run Code Online (Sandbox Code Playgroud)

seq有两个参数 - 它返回第二个参数,但只有在强制第一个到弱头范式(WHNF)之后,也就是在找出最外面的构造函数之后.这有x足够的力量EmptyListException来提升,所以catch可以做到这一点:

? mySafeHead' []
Nothing
Run Code Online (Sandbox Code Playgroud)


Sho*_*hoe 5

您可以evaluate在执行纯计算时用于捕获异常:

mySafeHead :: [a] -> IO (Maybe a)
mySafeHead xs = mySafeHead' xs `catch` handler
    where  
        mySafeHead' :: [a] -> IO (Maybe a)
        mySafeHead' ls = do
            x <- evaluate $ myHead ls
            return $ Just x
        handler :: EmptyListException -> IO (Maybe a)
        handler ex = return Nothing
Run Code Online (Sandbox Code Playgroud)

Live demo

  • @immibis无论如何都要在IO中捕获异常...所以你不会丢失任何东西. (3认同)