Why doesn't GHC give a compile time warning for the "No match in record selector" exception?
ghc, haskell
Solution
Is there [a] tool that can help me to prevent this type of error?
No, but there could be.
As you know, record syntax automatically generates getters with the same name as the attributes you define. Therefore the code
data Person = Adult { pName :: String}
| Kid { pName :: String
, pAge :: Int
} deriving Show
creates the functions `pName :: Person -> String` and `pAge :: Person -> Int`. Now, suppose Haskell had subtyping. If it did, then `Kid` could be a subtype of `Person`, and `pAge` could have the more appropriate type `Kid -> String`. However, Haskell does not have subtyping, and there is therefore no `Kid` type.
Now, given that `Person -> String` is the most specific type we can give to `pAge`, why not warn that `pAge` is a partial function at compile time? Let me divert the question by referring to the List example
data List a = Cons { head :: a, tail :: List a } | Empty
In this example, `head` and `tail` are partial functions: the two components of a non-empty list, but (due to Haskell's lack of subtyping) meaningless accessors on the empty list. So, why no warning by default? Well, by default, you know the code you have written. The compiler doesn't provide warnings if you use `unsafePerformIO`, because you're the programmer here, you're expected to use such things responsibly.
So tl;dr: if you want the warning here:
getAge :: Person -> Int
getAge p = pAge p
then you're out of luck, because the type system does not have enough information to deduce that this is a problem.
If you want the warning here:
data Person = Adult | Kid { pAge :: Int }
then I'm sure it would be trivial to implement: just check that a given field exists in some constructors but not others. But I do not foresee this warning being widely useful for everyone; some might complain that it would be just noise.
Problem
When I run this buggy code... ``` 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 ``` ... I get this in ghci: ``` names: ["fred","john"] ages: [5,* * * Exception: No match in record selector pAge ``` I know how to avoid this error, but I'm wondering why compiling with "ghc -Wall" didn't warn me about this problem. Is there another tool that can help me to prevent this type of error?