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

Tad*_*Tad 11 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
Run Code Online (Sandbox Code Playgroud)

...我在ghci得到这个:

names: ["fred","john"]

ages: [5,* * * Exception: No match in record selector pAge
Run Code Online (Sandbox Code Playgroud)

我知道如何避免这个错误,但我想知道为什么编译"ghc -Wall"没有警告我这个问题.还有其他工具可以帮助我防止这种类型的错误吗?

Dan*_*ton 6

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

不,但可能会.

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

data Person = Adult { pName :: String}
            | Kid   { pName :: String
                    , pAge  :: Int
                    } deriving Show
Run Code Online (Sandbox Code Playgroud)

创造功能pName :: Person -> StringpAge :: Person -> Int.现在,假设 Haskell有子类型.如果确实如此,则Kid可以是子类型Person,并且pAge可以具有更合适的类型Kid -> String.然而,哈斯克尔也不会有子类型,因此不存在Kid类型.

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

data List a = Cons { head :: a, tail :: List a } | Empty
Run Code Online (Sandbox Code Playgroud)

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

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

getAge :: Person -> Int
getAge p = pAge p
Run Code Online (Sandbox Code Playgroud)

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

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

data Person = Adult | Kid { pAge :: Int }
Run Code Online (Sandbox Code Playgroud)

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

  • 这里不一定非常需要子类型.如果他愿意,@ Tad可以有一个`Adult`和一个`Kid`类型.如果他想要一个用于访问年龄的公共接口,他可以使用类型类或"人"类型. (2认同)
  • Dan 说:那么,为什么默认没有警告?嗯,默认情况下,您知道自己编写的代码。如果您使用 unsafePerformIO,编译器不会提供警告,因为您是这里的程序员,您应该负责任地使用这些东西。 - 那是真实的。但是 -Wall 警告我关于阴影变量和其他我喜欢用 -f-no-warn 关闭的警告......在这种特殊情况下,因为这是我第一次犯这个错误,我认为警告会有所帮助. 在我看来,这比运行时异常更可取。正如 Daniel Wagner 建议的那样,我可以提交 Trac 功能请求。 (2认同)