我有以下代码:
{-# 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;但是在这里我们选择了一个精确的,一个不精确的例外,因此不应该适用警告。
答案 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}}除了可能抛出之外什么都不做,它决定顺序无关紧要。
(对不起,不是真正的答案,但试着在评论中说明这一点;)