Haskell:在异常情况下不返回任何内容

Haskell:在异常情况下不返回任何内容,haskell,try-catch,maybe,Haskell,Try Catch,Maybe,我想返回xs的第一个元素(如果有)。 否则我想返回“Nothing”,但它返回以“Just”包装的异常。 为什么呢? 注意:我必须在mySaveHead中使用myHead,在执行纯计算时,您可以使用evaluate捕获异常: {-# LANGUAGE DeriveDataTypeable, ScopedTypeVariables #-} import Data.Typeable import Control.Exception data EmptyListException = EmptyL

我想返回xs的第一个元素(如果有)。 否则我想返回“Nothing”,但它返回以“Just”包装的异常。 为什么呢?
注意:我必须在
mySaveHead
中使用
myHead

在执行纯计算时,您可以使用

evaluate
捕获异常:

{-# 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)
mySafeHead::[a]->IO(可能是a)
mySafeHead xs=mySafeHead'xs`catch`handler
哪里
我的安全头“::[a]->IO(可能是a)
我的安全头'ls=do
x IO(可能是a)
handler ex=不返回任何内容

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

{-# 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)
mySafeHead::[a]->IO(可能是a)
mySafeHead xs=mySafeHead'xs`catch`handler
哪里
我的安全头“::[a]->IO(可能是a)
我的安全头'ls=do
x IO(可能是a)
handler ex=不返回任何内容

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

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
我理解为什么您会将其描述为“它返回以“Just”包装的异常”,但事实并非如此

Haskell是非严格的,所以它推迟计算,直到需要一个值

mySafeHead
中,未检查
myHead xs
的值,因此未对其进行评估。取而代之的是,该值的计算被保留为thunk,它被包装在
Just
中并返回

然后,在
ghci
中,当尝试打印值时,最终强制该thunk,并引发异常。因为我们现在已经远远超出了
catch
语句的范围,所以它不适用,异常会一直延伸到终端,在终端中断输出的打印

解决此问题的简单方法是在退出
catch
语句之前,使用
seq
强制计算
myHead xs

λ mySafeHead []
Just *** Exception: EmptyListException
seq
接受两个参数-它返回第二个参数,但仅在强制第一个参数为弱头范式(WHNF)之后,也就是说,在找出最外层的构造函数是什么之后。这就迫使
x
足以引发
EmptyListException
,因此
catch
可以执行以下操作:

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

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

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
我理解为什么您会将其描述为“它返回以“Just”包装的异常”,但事实并非如此

Haskell是非严格的,所以它推迟计算,直到需要一个值

mySafeHead
中,未检查
myHead xs
的值,因此未对其进行评估。取而代之的是,该值的计算被保留为thunk,它被包装在
Just
中并返回

然后,在
ghci
中,当尝试打印值时,最终强制该thunk,并引发异常。因为我们现在已经远远超出了
catch
语句的范围,所以它不适用,异常会一直延伸到终端,在终端中断输出的打印

解决此问题的简单方法是在退出
catch
语句之前,使用
seq
强制计算
myHead xs

λ mySafeHead []
Just *** Exception: EmptyListException
seq
接受两个参数-它返回第二个参数,但仅在强制第一个参数为弱头范式(WHNF)之后,也就是说,在找出最外层的构造函数是什么之后。这就迫使
x
足以引发
EmptyListException
,因此
catch
可以执行以下操作:

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

如果您只想在 列表是非空的,否则
什么都没有
,明智的方法是 根本不使用异常(无论是由您显式抛出,还是 由于调用空列表上的
head::[a]->a而引发)。相反,请定义一个总函数:

或者,从安全包装中使用


前奏曲
中存在诸如
头部
尾部
等非全部函数是一个历史错误。希望有一天它们会被弃用,并最终被删除。

如果您只想在 列表是非空的,否则
什么都没有
,明智的方法是 根本不使用异常(无论是由您显式抛出,还是 由于调用空列表上的
head::[a]->a而引发)。相反,请定义一个总函数:

或者,从安全包装中使用


前奏曲
中存在诸如
头部
尾部
等非全部函数是一个历史错误。希望有一天它们会被弃用,并最终被删除。

但结果的计算是不纯的(
IO
monad)。@immibis捕获异常无论如何都必须在IO中。。。所以你不会真的丢失任何东西。但是结果是不纯净的(
IO
monad)。@immibis捕获异常无论如何必须在IO中。。。所以你不会真的失去任何东西。这个作业很愚蠢。真正地在几乎所有情况下,在纯代码中故意抛出异常都是一个坏主意。也许这不是故意的?也许它只是对给定的东西进行建模,并且有它自己的好理由抛出异常?这个赋值是一个愚蠢的赋值。真正地在几乎所有情况下,在纯代码中故意抛出异常都是一个坏主意。也许这不是故意的?也许它只是对给定的东西进行了建模,并有自己的好理由抛出异常?奇怪,你的初始输出。愤怒的鞋子:以前从未使用过该网站,所以不知道为什么它的工作方式与我的机器不同(可能它在退出时没有刷新STDOUT,但有一点模式匹配。奇怪,你的初始输出。愤怒的鞋子:以前从未使用过那个网站,所以不确定为什么它的工作方式与我的机器不同(可能它在运行时不会刷新STDOUT)