当我运行这个有问题的代码时......
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"没有警告我这个问题。是否有其他工具可以帮助我防止此类错误?
答案 0 :(得分:6)
是否有[a]工具可以帮助我防止此类错误?
不,但可能有。
如您所知,记录语法会自动生成与您定义的属性同名的getter。因此代码
data Person = Adult { pName :: String}
| Kid { pName :: String
, pAge :: Int
} deriving Show
创建函数pName :: Person -> String
和pAge :: 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
在此示例中,head
和tail
是部分函数:非空列表的两个组件,但(由于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)
从 8.4 开始,使用 -Wpartial-fields
。