13

当我运行这个错误的代码时......

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”编译没有警告我这个问题。是否有其他工具可以帮助我防止此类错误?

4

3 回答 3

8

是否有 [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 没有类型,因此没有Kid类型。

现在,鉴于这Person -> String是我们可以给予的最具体的类型,为什么不在编译时pAge警告它是一个偏函数呢?pAge让我通过参考List示例来转移问题

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

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

所以tl;博士:如果你想要这里的警告:

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

那么你就不走运了,因为类型系统没有足够的信息来推断这是一个问题。

如果你想要这里的警告:

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

那么我确信实现起来很简单:只需检查给定字段是否存在于某些构造函数中,但不存在于其他构造函数中。但我不认为这个警告对每个人都有广泛的用处。有些人可能会抱怨这只是噪音。

于 2012-05-01T07:50:52.927 回答
2

如果http://community.haskell.org/~ndm/catch/没有收到这个,我会感到惊讶。

于 2012-05-01T11:15:10.150 回答
2

从 8.4 开始,它确实与-Wpartial-fields.

https://downloads.haskell.org/ghc/latest/docs/html/users_guide/using-warnings.html#ghc-flag--Wpartial-fields

于 2021-04-06T01:53:07.720 回答