Haskell编译器能否对使用"undefined"的函数发出警告?

14
如果我使用 "undefined" 定义一个函数,它可以通过类型检查。
add2 :: Int -> Int -> Int
add2 = undefined

有没有可能检测到任何函数在函数定义中使用“undefined”,并将其转换为警告?

在开发阶段,使用“undefined”来检查类型签名是否正确,在实现函数之前这很有用。然后在生产环境中,我可以有一些方法来捕获我忘记为具有“undefined”的函数提供实现的错误。


3
为什么不在源代码中执行 grep 命令? undefined 是一个非常奇怪的变量名。 - Willem Van Onsem
5
在CI中机械地执行-Werror指令可以更容易地实现零警告,并且警告会与已经处理编译器警告的编辑器工具很好地集成。手动使用grep则要不太正规得多。 - Alexis King
如果我说错了,请纠正我,我认为 GHC 没有任何选项可以警告使用 undefined,这就是 @willem-van-onsem 正在尝试解决的问题。虽然我学到了类型洞比 undefined 更好地实现了我想要做的事情。 - Leo Zhang
3个回答

21
一个好的解决方案是使用类型占位符来代替 undefined,并使用 -fdefer-typed-holes 编译器标志使它们成为警告而不是错误(这通常更有用)。启用此标志后,您应该像这样编写示例:
add2 :: Int -> Int -> Int
add2 = _

...它会产生以下警告:

warning: [-Wtyped-holes]
    • Found hole: _ :: Int -> Int -> IntIn the expression: _
      In an equation for ‘add2’: add2 = _
    • Relevant bindings include
        add2 :: Int -> Int -> Int

现代的GHC甚至会在警告中列出洞可能的替换选项:

      Valid substitutions include
        add2 :: Int -> Int -> Int
        (+) :: forall a. Num a => a -> a -> a
          (imported from ‘Prelude’ (and originally defined in ‘GHC.Num’))
        (*) :: forall a. Num a => a -> a -> a
          (imported from ‘Prelude’ (and originally defined in ‘GHC.Num’))
        (^) :: forall a b. (Num a, Integral b) => a -> b -> a
          (imported from ‘Prelude’ (and originally defined in ‘GHC.Real’))
        (-) :: forall a. Num a => a -> a -> a
          (imported from ‘Prelude’ (and originally defined in ‘GHC.Num’))
        seq :: forall a b. a -> b -> b
          (imported from ‘Prelude’ (and originally defined in ‘GHC.Prim’))
        (Some substitutions suppressed; use -fmax-valid-substitutions=N or -fno-max-valid-substitutions)

4

1
值得指出的是,如果您查看源代码,您会发现它只是 undefined = Prelude.undefined;新定义允许添加警告编译指示,因此您可以轻松地创建自己的 undefined 版本并添加警告。将其放在自定义预处理器中的好处是,通常您会设置整个项目来使用它,这使得意外使用标准版本而不是新版本更加困难(后者不会触发警告)。 - Ben

3

如果您正在使用hlint,您可以将此规则添加到您的.hlint.yaml文件中:

- warning: {lhs: "undefined", rhs: undefined}

对于诸如以下警告:

使用其他选项。替换为:未定义

您可以使用name属性添加自己的描述,例如:

- warning: {lhs: "undefined", rhs: undefined, name: Realisation is required}

需要进行实现。替换为: undefined

您可以安全地使用自动重构,因为它只是将undefined替换为undefined,不会破坏任何东西。

此外,您可以忽略特定函数的此规则(使用name属性):

{-# ANN functionName "HLint: ignore Realisation is required" #-}
functionName :: Int -> Int
functionName x = undefined

或者针对模块:{-# ANN module "HLint: ignore Realisation is required" #-}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接