异常如何在Haskell中起作用(第二部分)?

时间:2013-05-21 19:41:19

标签: 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 (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

在使用优化开启和关闭编译时,它的行为有所不同:

crabgrass:~/tmp/signserv/src% ghc -fforce-recomp Main.hs && ./Main
Main: WrongArgumentCount
crabgrass:~/tmp/signserv/src% ghc -O -fforce-recomp Main.hs && ./Main
Main: Main.hs:20:9-34: Irrefutable pattern failed for pattern [portStr, cert, pw]

这是为什么?我知道imprecise exceptions can be chosen from arbitrarily;但是在这里我们选择了一个精确的,一个不精确的例外,因此不应该适用警告。

1 个答案:

答案 0 :(得分:14)

我同意哈马尔,这看起来像个臭虫。从一段时间以来,它似乎在HEAD中得到了解决。如果使用较早的ghc-7.7.20130312以及今天的HEAD ghc-7.7.20130521,则会引发WrongArgumentCount异常,并删除main的所有其他代码(欺骗优化者)。然而,仍然在7.6.3中被打破。

7.2系列的行为发生了变化,我从7.0.4得到了预期的WrongArgumentCount,并且(优化的)核心清楚地表明了这一点:

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不同时,请加WrongArgumentCount,否则请尝试完成其余操作。

使用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 ->

,其中

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]"

由于throwIO应该尊重IO行为的排序,

  

throwIO变体应该优先使用throw来引发IO monad中的异常,因为它保证了对其他IO操作的排序,而throw则没有。< / p>

不应该发生。

您可以使用NOINLINE的{​​{1}}变体强制执行正确的排序,或者在投掷前执行有效的when操作,因此看起来内联者看到{ {1}}除了可能抛出之外什么都不做,它决定顺序无关紧要。

(对不起,不是真正的答案,但试着在评论中说明这一点;)