2012-05-01 9 views
11

Quando eseguo questo codice buggy ...Perché GHC non fornisce un avviso di compilazione per l'eccezione "Nessuna corrispondenza nel selettore di record"?

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 

... ottengo questo in ghci:

names: ["fred","john"] 

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

so come evitare questo errore, ma mi chiedo il motivo per cui la compilazione con "ghc -Wall" non mi ha avvisato di questo problema. C'è un altro strumento che può aiutarmi a prevenire questo tipo di errore?

+3

Scommetto che la gente del GHC sarebbe disposta a mettere un avvertimento per questo. Dovresti presentare una richiesta di funzione su [Trac] (http://hackage.haskell.org/trac/ghc/newticket?type=feature+request)! –

+2

Esiste un sentimento generale di insoddisfazione nei confronti di record nella loro forma attuale. Alcune persone stanno studiando alternative. Nel frattempo, i record dei costruttori multipli non sono raccomandati e dovrebbero essere un avvertimento :-) –

+0

http://hackage.haskell.org/trac/ghc/ticket/7169 - potresti aggiungerti a cc. – sdcvvc

risposta

6

C'è uno strumento che può aiutarmi a prevenire questo tipo di errore?

No, ma potrebbe esserci.

Come sapete, la sintassi di registrazione genera automaticamente getter con lo stesso nome degli attributi definiti. Pertanto il codice

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

crea le funzioni pName :: Person -> String e pAge :: Person -> Int. Ora, supponi Haskell ha avuto sottotipizzazione. Se così fosse, allora Kid potrebbe essere un sottotipo di Person e pAge potrebbe avere il tipo più appropriato Kid -> String. Tuttavia, Haskell fa non ha sottotipo, e quindi non esiste il tipo Kid.

Ora, dato che Person -> String è il tipo più specifico che possiamo dare a pAge, perché non avvisare che pAge è una funzione parziale in fase di compilazione? Mi permetta di deviare le domande per riferimento all'esempio Elenco

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

In questo esempio, head e tail sono funzioni parziali: i due componenti di una lista non vuota, ma (a causa della mancanza di sottotipo Haskell) di accesso senza senso nella lista vuota Quindi, perché nessun avviso di default? Bene, per impostazione predefinita, conosci il codice che hai scritto. Il compilatore non fornisce avvisi se si utilizza unsafePerformIO, perché qui si è il programmatore, si prevede di utilizzare tali cose in modo responsabile.

Così tl; dr: se si desidera che l'avviso qui:

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

allora sei fuori di fortuna, perché il sistema di tipo non dispone di informazioni sufficienti per dedurre che questo è un problema.

Se si desidera che l'avviso qui:

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

allora sono sicuro che sarebbe banale da implementare: basta verificare che un determinato campo esiste in alcuni costruttori ma non altri. Ma non prevedo che questo avvertimento sia ampiamente utile per tutti; alcuni potrebbero lamentarsi che sarebbe solo rumore.

+0

Il compilatore potrebbe notare, tuttavia, che * pAge * è una funzione parziale e potrebbe avvisare gli utenti di tale funzione. Tuttavia, ci sono casi in cui la totalità delle funzioni non può essere riconosciuta, quindi si otterrebbero anche avvertimenti lì. – Ingo

+2

La sottotipizzazione non è strettamente necessaria qui. @Tad può avere un tipo 'Adult' e un' Kid' se lo desidera. Se vuole un'interfaccia comune per l'accesso alle età, può usare una classe tipo o un tipo 'Person'. –

+0

Dan ha detto: Quindi, perché nessun avviso di default? Bene, per impostazione predefinita, conosci il codice che hai scritto. Il compilatore non fornisce avvertimenti se usi unsafePerformIO, perché qui sei il programmatore, ci si aspetta che tu usi tali cose in modo responsabile. - – Tad

Problemi correlati