Exception 异常在Haskell中是如何工作的(第二部分)?

Exception 异常在Haskell中是如何工作的(第二部分)?,exception,haskell,Exception,Haskell,我有以下代码: {-# LANGUAGE DeriveDataTypeable #-} import Prelude hiding (catch) import Control.Exception (throwIO, Exception) import Control.Monad (when) import Data.Maybe import Data.Word (Word16) import Data.Typeable (Typeable) import System.Environment

我有以下代码:

{-# LANGUAGE DeriveDataTypeable #-}
import Prelude hiding (catch)
import Control.Exception (throwIO, Exception)
import Control.Monad (when)
import Data.Maybe
import Data.Word (Word16)
import Data.Typeable (Typeable)
import System.Environment (getArgs)

data ArgumentParserException = WrongArgumentCount | InvalidPortNumber
    deriving (Show, Typeable)

instance Exception ArgumentParserException

data Arguments = Arguments Word16 FilePath String

main = do
    args <- return []
    when (length args /= 3) (throwIO WrongArgumentCount)

    let [portStr, cert, pw] = args
    let portInt = readMaybe portStr :: Maybe Integer
    when (portInt == Nothing) (throwIO InvalidPortNumber)

    let portNum = fromJust portInt
    when (portNum < 0 || portNum > 65535) (throwIO InvalidPortNumber)

    return $ Arguments (fromInteger portNum) cert pw

-- Newer 'base' has Text.Read.readMaybe but alas, that doesn't come with
-- the latest Haskell platform, so let's not rely on it
readMaybe :: Read a => String -> Maybe a
readMaybe s = case reads s of
    [(x, "")] -> Just x
    _         -> Nothing

为什么会这样?我知道,;但是这里我们从一个精确的和一个不精确的异常中选择,所以警告不应该适用。

我同意哈马尔的观点,这看起来像一个bug。而且它似乎从一段时间以来就固定在脑海里了。对于较旧的
ghc-7.7.20130312
以及今天的头
ghc-7.7.20130521
,将引发
ErrorArgumentCount
异常,并删除
main
的所有其他代码(优化器的bully)。然而,仍然在7.6.3中被打破

7.2系列的行为发生了变化,我从7.0.4中获得了预期的
ErrorArgumentCount
,并且(优化的)核心明确了这一点:

Main.main1 =
  \ (s_a11H :: GHC.Prim.State# GHC.Prim.RealWorld) ->
    case GHC.List.$wlen
           @ GHC.Base.String (GHC.Types.[] @ GHC.Base.String) 0
    of _ {
      __DEFAULT ->
        case GHC.Prim.raiseIO#
               @ GHC.Exception.SomeException @ () Main.main7 s_a11H
        of _ { (# new_s_a11K, _ #) ->
        Main.main2 new_s_a11K
        };
      3 -> Main.main2 s_a11H
    }
当空列表的长度不同于3时,请提高
ErrorArgumentCount
,否则尝试执行其余操作

对于7.2及更高版本,长度的计算移到分析
portStr
之后:

Main.main1 =
  \ (eta_Xw :: GHC.Prim.State# GHC.Prim.RealWorld) ->
    case Main.main7 of _ {
      [] -> case Data.Maybe.fromJust1 of wild1_00 { };
      : ds_dTy ds1_dTz ->
        case ds_dTy of _ { (x_aOz, ds2_dTA) ->
        case ds2_dTA of _ {
          [] ->
            case ds1_dTz of _ {
              [] ->
                case GHC.List.$wlen
                       @ [GHC.Types.Char] (GHC.Types.[] @ [GHC.Types.Char]) 0
                of _ {
                  __DEFAULT ->
                    case GHC.Prim.raiseIO#
                           @ GHC.Exception.SomeException @ () Main.main6 eta_Xw
                    of wild4_00 {
                    };
                  3 ->
在哪里

因为应该尊重
IO
操作的顺序

throwIO
变量应优先用于throw,以在
IO
monad中引发异常,因为它保证了相对于其他
IO
操作的排序,而throw不保证

这不应该发生

您可以通过使用
when
NOINLINE
变体,或者在抛出之前执行有效的
IO
操作,强制执行正确的顺序,因此,当内联线看到
when
除了可能抛出之外什么都不做时,它似乎认为顺序无关紧要


(很抱歉,这不是一个真正的答案,但请尝试在评论中加入这一点;)

我觉得这似乎是一个bug。您正在使用哪个GHC版本?我在GHC 7.6.2中看到了同样的行为。@hammar它至少发生在7.6.1和7.4.1中,在#haskell中提出它的人使用的是7.0.x。@DanielWagner这很奇怪,因为在7.0.2和7.0.4中,我得到了
错误的argumentcount
。(也是6.12.3)@DanielFischer Hm,那么我的记忆可能有问题。无论如何,我知道它既不是7.4.1也不是7.6.1,因为我必须更改他发给我的阴谋集团文件,以放松对基地的依赖我已经可以减少了,谢谢。提出了一个错误;我们将看看GHC开发人员是否同意。您可能会了解到它已经被修复了,我刚刚尝试了HEAD,并且按照预期工作。
Main.main1 =
  \ (eta_Xw :: GHC.Prim.State# GHC.Prim.RealWorld) ->
    case Main.main7 of _ {
      [] -> case Data.Maybe.fromJust1 of wild1_00 { };
      : ds_dTy ds1_dTz ->
        case ds_dTy of _ { (x_aOz, ds2_dTA) ->
        case ds2_dTA of _ {
          [] ->
            case ds1_dTz of _ {
              [] ->
                case GHC.List.$wlen
                       @ [GHC.Types.Char] (GHC.Types.[] @ [GHC.Types.Char]) 0
                of _ {
                  __DEFAULT ->
                    case GHC.Prim.raiseIO#
                           @ GHC.Exception.SomeException @ () Main.main6 eta_Xw
                    of wild4_00 {
                    };
                  3 ->
Main.main7 =
  Text.ParserCombinators.ReadP.run
    @ GHC.Integer.Type.Integer Main.main8 Main.main3

Main.main8 =
  GHC.Read.$fReadInteger5
    GHC.Read.$fReadInteger_$sconvertInt
    Text.ParserCombinators.ReadPrec.minPrec
    @ GHC.Integer.Type.Integer
    (Text.ParserCombinators.ReadP.$fMonadP_$creturn
       @ GHC.Integer.Type.Integer)

Main.main3 = case lvl_r1YS of wild_00 { }

lvl_r1YS =
  Control.Exception.Base.irrefutPatError
    @ ([GHC.Types.Char], [GHC.Types.Char], [GHC.Types.Char])
    "Except.hs:21:9-34|[portStr, cert, pw]"