Haskell: Return 异常时无
Haskell: Return Nothing in case of exception
{-# 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)
我想要return xs 的第一个元素(如果有的话)。
否则我想 return "Nothing",但它 return 是包裹在 "Just" 中的异常。
这是为什么?
P.S.: 我必须在 mySaveHead
.
中使用 myHead
您可以使用 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
所以当我运行你的代码时,这就是我看到的
λ mySafeHead []
Just *** Exception: EmptyListException
我理解您为什么将此描述为 "it returns the Exception wrapped in "只是“.”,但实际上并不是这样。
Haskell 是非严格的,因此它会推迟计算直到需要一个值。
在 mySafeHead
中,myHead xs
的值未被检查,因此未被评估。相反,该值的计算保留为 thunk,它包装在 Just
中并返回。
然后,在 ghci
中,thunk 在尝试打印值时最终被强制执行,并引发异常。由于我们现在完全超出了 catch
语句的范围,因此它不适用,并且异常一直到终端,在那里它中断了输出的打印。
解决此问题的简单方法是在退出 catch
语句之前使用 seq
强制计算 myHead xs
:
mySafeHead' :: [a] -> IO (Maybe a)
mySafeHead' xs = (let x = myHead xs in x `seq` return (Just x))
`catch`
(\(_::EmptyListException) -> return Nothing)
seq
接受两个参数 - returns 第二个,但仅在将第一个参数强制为弱头范式 (WHNF) 之后,即在找出最外层的构造函数之后。这迫使 x
足以使 EmptyListException
升高,因此 catch
可以做它的事情:
λ mySafeHead' []
Nothing
如果你想要的只是让列表的头部成为 Just a
当
列表是非空的,否则 Nothing
,明智的做法是
根本不使用异常(无论是你明确抛出的,还是
由于在空列表上调用 head :: [a] -> a
而抛出)。相反,定义一个 total function:
mySafeHead :: [a] -> Maybe a
mySafeHead [] = Nothing
mySafeHead (a:_) = Just a
或者,使用 safe 包中的 headMay
。
Prelude
中存在非总函数,例如 head
和 tail
是一个历史错误。希望有一天它们会被弃用并最终被删除。
{-# 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)
我想要return xs 的第一个元素(如果有的话)。
否则我想 return "Nothing",但它 return 是包裹在 "Just" 中的异常。
这是为什么?
P.S.: 我必须在 mySaveHead
.
myHead
您可以使用 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
所以当我运行你的代码时,这就是我看到的
λ mySafeHead []
Just *** Exception: EmptyListException
我理解您为什么将此描述为 "it returns the Exception wrapped in "只是“.”,但实际上并不是这样。
Haskell 是非严格的,因此它会推迟计算直到需要一个值。
在 mySafeHead
中,myHead xs
的值未被检查,因此未被评估。相反,该值的计算保留为 thunk,它包装在 Just
中并返回。
然后,在 ghci
中,thunk 在尝试打印值时最终被强制执行,并引发异常。由于我们现在完全超出了 catch
语句的范围,因此它不适用,并且异常一直到终端,在那里它中断了输出的打印。
解决此问题的简单方法是在退出 catch
语句之前使用 seq
强制计算 myHead xs
:
mySafeHead' :: [a] -> IO (Maybe a)
mySafeHead' xs = (let x = myHead xs in x `seq` return (Just x))
`catch`
(\(_::EmptyListException) -> return Nothing)
seq
接受两个参数 - returns 第二个,但仅在将第一个参数强制为弱头范式 (WHNF) 之后,即在找出最外层的构造函数之后。这迫使 x
足以使 EmptyListException
升高,因此 catch
可以做它的事情:
λ mySafeHead' []
Nothing
如果你想要的只是让列表的头部成为 Just a
当
列表是非空的,否则 Nothing
,明智的做法是
根本不使用异常(无论是你明确抛出的,还是
由于在空列表上调用 head :: [a] -> a
而抛出)。相反,定义一个 total function:
mySafeHead :: [a] -> Maybe a
mySafeHead [] = Nothing
mySafeHead (a:_) = Just a
或者,使用 safe 包中的 headMay
。
Prelude
中存在非总函数,例如 head
和 tail
是一个历史错误。希望有一天它们会被弃用并最终被删除。