2012-05-01 1 views
11

Wenn ich diesen Buggy Code ausführen ...Warum gibt GHC keine Kompilierungswarnung für die Ausnahme "Keine Übereinstimmung in Datensatzauswahl"?

data Person = Adult { pName :: String} 
      | Kid { pName :: String 
        , pAge :: Int 
        } deriving Show 

getAge :: Person -> Int 
getAge p = pAge p 

getName :: Person -> String 
getName p = pName p 

main :: IO() 
main = do 

    let p1 = Kid "fred" 5 
     p2 = Adult "john" 
     ps = [p1, p2] 

    names = map getName ps 
    ages = map getAge ps 

    putStrLn $ "names: " ++ show names 
    putStrLn $ "ages: " ++ show ages 

... ich dies in GHCI:

names: ["fred","john"] 

ages: [5,* * * Exception: No match in record selector pAge 

Ich weiß, wie man diese Fehler zu vermeiden, aber ich frage mich, warum Sie mit dem Kompilieren "ghc -Wall" hat mich nicht vor diesem Problem gewarnt. Gibt es ein anderes Tool, das mir helfen kann, diese Art von Fehlern zu vermeiden?

+3

Ich wette, die GHC-Leute wären bereit, dafür eine Warnung zu kleben. Sie sollten eine Feature-Anfrage auf [Trac] (http://hackage.haskell.org/trac/ghc/newticket?type=feature+request) einreichen! –

+2

Es gibt ein allgemeines Gefühl der Unzufriedenheit um Rekorde in ihrer gegenwärtigen Form. Einige Leute untersuchen Alternativen. In der Zwischenzeit sind Mehrfachkonstruktor-Aufzeichnungen nicht empfehlenswert und sollten eine Warnung ihrer eigenen sein :-) –

+0

http://hackage.haskell.org/trac/ghc/ticket/7169 - Sie könnten sich selbst zu cc hinzufügen. – sdcvvc

Antwort

6

Gibt es [ein] Werkzeug, das mir helfen kann, diese Art von Fehler zu verhindern?

Nein, aber es könnte sein.

Wie Sie wissen, generiert die Syntax für Datensätze automatisch Getter mit demselben Namen wie die von Ihnen definierten Attribute. Daher ist der Code

data Person = Adult { pName :: String} 
      | Kid { pName :: String 
        , pAge :: Int 
        } deriving Show 

schafft die Funktionen pName :: Person -> String und pAge :: Person -> Int. Nun, angenommen Haskell hatte Subtyping. Wenn dies der Fall wäre, könnte Kid ein Subtyp von Person sein, und pAge könnte den geeigneteren Typ Kid -> String haben. Haskell hat jedoch nicht haben Subtyping, und es gibt daher keine Kid Art.

nun gegeben, dass Person -> String die spezifische Art ist, dass wir zu pAge geben können, warnen, warum nicht, dass pAge eine Teilfunktion bei der Kompilierung ist? Lassen Sie mich durch Bezugnahme auf die Liste Beispiel

data List a = Cons { head :: a, tail :: List a } | Empty 

In diesem Beispiel die Frage abzulenken, head und tail sind Teilfunktionen: Die beiden Komponenten einer nicht-leere Liste, aber (wegen Haskell Mangel an Subtyping) bedeutungslos Accessoren auf der leeren Liste. Also, warum keine Warnung standardmäßig? Nun, standardmäßig kennen Sie den Code, den Sie geschrieben haben. Der Compiler gibt keine Warnungen aus, wenn Sie unsafePerformIO verwenden. Da Sie der Programmierer hier sind, wird erwartet, dass Sie solche Dinge verantwortungsvoll verwenden.

So tl; dr: Wenn Sie die Warnung hier wollen:

getAge :: Person -> Int 
getAge p = pAge p 

dann bist du kein Glück, da das Typsystem nicht genügend Informationen, um ableiten, dass dies ein Problem ist.

Wenn Sie die Warnung hier wollen:

data Person = Adult | Kid { pAge :: Int } 

dann würde ich bin sicher, dass es trivial zu implementieren sein: nur prüfen, ob ein bestimmtes Feld in einigen Konstrukteuren existiert, andere aber nicht. Aber ich sehe nicht ein, dass diese Warnung für alle von großem Nutzen ist. Einige könnten sich beschweren, dass es nur Lärm wäre.

+0

Der Compiler konnte jedoch feststellen, dass * pAge * eine Teilfunktion ist und Benutzer dieser Funktion warnen können. Es gibt jedoch Fälle, in denen die Totalität von Funktionen nicht erkannt werden kann, daher würde man dort auch Warnungen erhalten. – Ingo

+2

Subtyping ist hier nicht unbedingt notwendig. @Tad kann einen 'Adult' und einen' Kid' Typ haben, wenn er möchte. Wenn er eine gemeinsame Schnittstelle für den Zugriff auf Alter haben möchte, kann er eine Typklasse oder eine Art Person verwenden. –

+0

Dan sagte: Also, warum keine Warnung standardmäßig? Nun, standardmäßig kennen Sie den Code, den Sie geschrieben haben. Der Compiler gibt keine Warnungen aus, wenn Sie unsafePerformIO verwenden. Da Sie der Programmierer sind, wird von Ihnen erwartet, dass Sie solche Dinge verantwortungsvoll verwenden. - – Tad