2013-05-21 9 views
17

Mam następujący kod:Jak działają wyjątki w Haskell (część druga)?

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

Jego zachowanie różni gdy skompilowany z optymalizacje włączenia i wyłączenia:

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] 

Dlaczego tak jest? Jestem świadomy, że imprecise exceptions can be chosen from arbitrarily; ale tutaj wybieramy jeden dokładny i jeden nieprecyzyjny wyjątek, więc zastrzeżenie nie powinno mieć zastosowania.

+0

To wygląda jak błąd do mnie. Którą wersję GHC używasz? Widzę to samo zachowanie w GHC 7.6.2. – hammar

+0

@hammar To się dzieje co najmniej 7.6.1 i 7.4.1 tutaj, a osoba, która go wychowała w #haskell, używała wersji 7.0.x. –

+0

@DanielWagner To dziwne, ponieważ na 7.0.2 i 7.0.4 otrzymuję 'WrongArgumentCount'. (Również 6.12.3) –

Odpowiedz

14

Zgodziłbym się z młotem, to wygląda jak błąd. Wydaje się, że naprawiono go w HEAD od pewnego czasu. Ze starszą wersją ghc-7.7.20130312, a także z dzisiejszą wersją HEAD ghc-7.7.20130521, wyjątek WrongArgumentCount jest podnoszony, a cały inny kod main jest usuwany (tyranizuj dla optymalizatora). Wciąż jednak złamane w 7.6.3.

zachowanie zmieniło się wraz z serii 7.2, pojawia się oczekiwany WrongArgumentCount z 7.0.4, a (zoptymalizowanej) rdzeń sprawia, że ​​jasne:

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 
    } 

gdy długość pustej liście różni się od 3 , podnieś WrongArgumentCount, w przeciwnym razie postaraj się zrobić resztę.

7,2 i później, ocenę długości jest przenoszony za parsowania 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 -> 

gdzie

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

Od throwIO ma respektować uporządkowanie IO działań

Wariant throwIO powinien być użyty zamiast rzucać, aby podnieść wyjątek w monadzie IO, ponieważ gwarantuje on porządkowanie w odniesieniu do innych operacji IO, natomiast rzut nie.

to nie powinno się zdarzyć.

Można wymusić poprawną kolejność za pomocą NOINLINE wariant when lub przez wykonanie effectful IO działania przed wyrzuceniem, więc wydaje się, że gdy Inliner widzi, że when nie robi nic, z wyjątkiem być może rzucać, uzna, że ​​zamówienie nie robi Nieważne.

(Niestety, nie jest prawdziwa odpowiedź, ale starają się dopasować, że w komentarzu;)

+1

Dzięki. Zgłoszony błąd [tutaj] (http://hackage.haskell.org/trac/ghc/ticket/7924); zobaczymy, czy twórcy GHC się zgodzą. –

+0

Prawdopodobnie dowiesz się, że jest już naprawiony, po prostu wypróbowałem HEAD i działa zgodnie z oczekiwaniami. –