2013-05-21 28 views
17

我有以下代碼:Haskell中的異常如何工作(第二部分)?

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] 

爲什麼是這樣的:

{-# 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 

其行爲因與時開啓和關閉的優化編譯?我知道imprecise exceptions can be chosen from arbitrarily;但是我們在這裏選擇的是一個精確的和一個不精確的例外,所以這個警告不應該適用。

+0

這看起來像是一個bug。你正在使用哪個GHC版本?我看到與GHC 7.6.2相同的行爲。 – hammar

+0

@hammar它至少在7.6.1和7.4.1這裏發生,並且在#haskell中提到它的人使用了7.0.x. –

+0

@DanielWagner這很奇怪,因爲在7.0.2和7.0.4上,我得到了'WrongArgumentCount'。 (也是6.12.3) –

回答

14

我會同意哈馬爾,這看起來像一個錯誤。自從一段時間以來,它似乎已經被修正了對於舊的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變種應該優先使用,以便在IO monad中拋出異常,因爲它保證相對於其他IO操作的排序,而throw不會。

應該不會發生。

您可以強制使用NOINLINE變種when正確的順序,或者通過拋出之前執行effectful IO動作,如此看來,當內襯看到了when什麼都不做可能除了扔,它決定順序沒有按沒關係。

(對不起,沒有一個真正的答案,但儘量使它符合評論;)

+1

謝謝。提交了一個錯誤[here](http://hackage.haskell.org/trac/ghc/ticket/7924);我們會看看GHC開發者是否同意。 –

+0

你可能會知道它已經被修復了,我只是試過HEAD,並且按照預期工作。 –