2012-05-01 80 views
11

Cuando ejecuto el código con errores ...¿Por qué GHC no da una advertencia de tiempo de compilación para la excepción "Sin coincidencia en el selector de registro"?

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 

... me sale esto en ghci:

names: ["fred","john"] 

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

sé cómo evitar este error, pero me pregunto por qué compilar con "ghc -Wall" no me advirtió sobre este problema. ¿Hay alguna otra herramienta que pueda ayudarme a prevenir este tipo de error?

+3

Apuesto a que la gente de GHC estaría dispuesta a poner una advertencia para esto. Debe presentar una solicitud de función en [Trac] (http://hackage.haskell.org/trac/ghc/newticket?type=feature+request). –

+2

Hay un sentimiento general de insatisfacción alrededor de los registros en su forma actual. Algunas personas están investigando alternativas. Mientras tanto, los registros de múltiples constructores no son recomendables, y deberían ser una advertencia propia :-) –

+0

http://hackage.haskell.org/trac/ghc/ticket/7169 - puede agregarse a la CC. – sdcvvc

Respuesta

6

¿Hay alguna herramienta que pueda ayudarme a evitar este tipo de error?

No, pero podría haber.

Como sabe, la sintaxis del registro genera automáticamente captadores con el mismo nombre que los atributos que define. Por lo tanto el código

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

crea las funciones pName :: Person -> String y pAge :: Person -> Int. Ahora, supongamos que Haskell tenía subtipos. Si así fuera, entonces Kid podría ser un subtipo de Person, y pAge podría tener el tipo más apropiado Kid -> String. Sin embargo, Haskell tiene no tiene subtipado, y por lo tanto no hay Kid tipo.

Ahora, dado que Person -> String es el tipo más específico que podemos dar a pAge, ¿por qué no advertir que pAge es una función parcial en tiempo de compilación? Permítanme Desviar la cuestión haciendo referencia al ejemplo de lista

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

En este ejemplo, head y tail son funciones parciales: los dos componentes de una lista no vacía, pero (debido a la falta de subtipos de Haskell) descriptores de acceso sin sentido en la lista vacía Entonces, ¿por qué no hay advertencia por defecto? Bueno, de manera predeterminada, conoces el código que has escrito. El compilador no proporciona advertencias si usa unsafePerformIO, porque usted es el programador aquí, se espera que use esas cosas de manera responsable.

Así tl; dr: si desea que la advertencia aquí:

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

entonces estás de suerte, ya que el sistema de tipo no tiene suficiente información para deducir que este es un problema.

Si desea que la advertencia aquí:

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

entonces estoy seguro de que sería trivial para aplicar: basta con comprobar que existe un campo dado en algunos constructores, pero otros no. Pero no preveo que esta advertencia sea ampliamente útil para todos; algunos podrían quejarse de que sería solo ruido.

+0

El compilador podría notar, sin embargo, que * pAge * es una función parcial, y podría advertir a los usuarios de esa función. Sin embargo, hay casos en que la totalidad de las funciones no puede ser reconocida, por lo tanto, uno también recibiría advertencias allí. – Ingo

+2

Subtipo no es estrictamente necesario aquí. @Tad puede tener un tipo 'Adulto' y 'Chico' si lo desea. Si quiere una interfaz común para acceder a las edades, puede usar una clase de tipo o un tipo 'Persona '. –

+0

Dan dijo: Entonces, ¿por qué no hay advertencia por defecto? Bueno, de manera predeterminada, conoces el código que has escrito. El compilador no proporciona advertencias si usa inseguroPerformIO, porque usted es el programador aquí, se espera que use esas cosas de manera responsable. - – Tad

Cuestiones relacionadas