为什么GHC没有为“记录选择器中的不匹配”例外提供编译时警告?

时间:2012-05-01 05:03:03

标签: haskell ghc

当我运行这个有问题的代码时......

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

...我在ghci中得到这个:

names: ["fred","john"]

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

我知道如何避免这个错误,但我想知道为什么要编译" ghc -Wall"没有警告我这个问题。是否有其他工具可以帮助我防止此类错误?

3 个答案:

答案 0 :(得分:6)

  

是否有[a]工具可以帮助我防止此类错误?

不,但可能有。

如您所知,记录语法会自动生成与您定义的属性同名的getter。因此代码

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

创建函数pName :: Person -> StringpAge :: Person -> Int。现在,假设 Haskell有子类型。如果是,则Kid可以是Person的子类型,pAge可以使用更合适的类型Kid -> String。但是,Haskell not 没有子类型,因此没有Kid类型。

现在,鉴于Person -> String是我们可以提供给pAge的最具体类型,为什么不在编译时警告pAge是部分函数?让我通过参考列表示例

来转移问题
data List a = Cons { head :: a, tail :: List a } | Empty

在此示例中,headtail是部分函数:非空列表的两个组件,但(由于Haskell缺少子类型)空列表中无意义的访问器。那么,为什么默认没有警告?嗯,默认情况下,您知道您编写的代码。如果你使用unsafePerformIO,编译器不会提供警告,因为你是这里的程序员,你应该负责任地使用这些东西。

所以 tl; dr :如果你想在这里发出警告:

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

然后你运气不好,因为类型系统没有足够的信息来推断这是一个问题。

如果你想在这里发出警告:

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

然后我确定实现它是微不足道的:只检查某些构造函数中是否存在给定字段,而不是其他构造函数。但我不认为这个警告对每个人都有用;有些人可能会抱怨这只是噪音。

答案 1 :(得分:2)

如果http://community.haskell.org/~ndm/catch/不接受这一点,我会感到惊讶。

答案 2 :(得分:1)