25

使用 启用 GHC 警告被认为是一种很好的做法-Wall。但是,我发现修复这些警告会对某些类型的代码结构产生负面影响。

示例 1:

f >>如果我没有明确使用以下_ <- f形式,使用等同于的 do-notation将生成警告:

Warning: A do-notation statement discarded a result of type Char.
         Suppress this warning by saying "_ <- f",
         or by using the flag -fno-warn-unused-do-bind

我知道我可能会忘记对f. 但是,忽略结果是合法的(在解析器中很常见)。使用时没有警告>>,对吧?使用_ <-比它应该更重。

示例 2:

使用与可见函数相同的名称命名模式变量将给出:

Warning: This binding for `map' shadows the existing binding
           imported from Prelude

当使用记录语法时,这会变得更糟,因为命名空间会很快被污染。解决方案是在模式表达式中提供一个替代名称。所以我最终使用了一个不太合适的名字来避免警告。我不觉得这是一个足够好的理由。

我知道我可以使用-fno-warn-...选项,但毕竟我应该坚持-Wall吗?

4

6 回答 6

25

示例 1:

我重新学会了以 Applicative 风格编写解析器——它们更加简洁。例如,而不是:

funCallExpr :: Parser AST
funCallExpr = do
    func <- atom
    token "("
    arg <- expr
    token ")"
    return $ FunCall func arg

我改为写:

funCallExpr :: Parser AST
funCallExpr = FunCall <$> atom <* token "(" <*> expr <* token ")"

但是我能说什么,如果您不喜欢该警告,请按照它的建议禁用它。

示例 2:

是的,我发现这个警告也有点烦人。但它救了我几次。

它与命名约定有关。我喜欢保持模块非常小,并保持大多数导入合格(除了“符号”导入Control.ApplicativeControl.Arrow)。这样可以降低名称冲突的可能性,并且使事情易于处理。 hothasktags如果您使用标签,则可以容忍这种风格。

如果您只是对具有相同名称的字段进行模式匹配,则可以使用-XNamedFieldPuns-XRecordWildCards重用该名称:

data Foo = Foo { baz :: Int, bar :: String }

-- RecordWildCards
doubleBaz :: Foo -> Int
doubleBaz (Foo {..}) = baz*baz

-- NamedFieldPuns
reverseBar :: Foo -> String
reverseBar (Foo {bar}) = reverse bar

另一个常见的约定是为唱片公司添加匈牙利语前缀:

data Foo = Foo { fooBaz :: Int, fooBar :: String }

但是,是的,在 Haskell 中使用记录并不有趣。无论如何,保持你的模块小,你的抽象紧凑,这不应该是一个问题。将其视为一个警告,上面写着simpleyyy, man

于 2010-11-14T07:17:49.833 回答
10

我认为使用-Wall可能会导致代码可读性降低。特别是,如果它正在做一些算术。

其他一些示例,其中使用-Wall建议修改可读性较差。

(^)with-Wall需要指数的类型签名

考虑这段代码:

norm2 x y = sqrt (x^2 + y^2)
main = print $ norm2 1 1

-Wall给出了两个这样的警告:

rt.hs:1:18:
    Warning: Defaulting the following constraint(s) to type `Integer'
             `Integral t' arising from a use of `^' at rt.hs:2:18-20
    In the first argument of `(+)', namely `x ^ 2'
    In the first argument of `sqrt', namely `(x ^ 2 + y ^ 2)'
    In the expression: sqrt (x ^ 2 + y ^ 2)

(^(2::Int)到处写而不是(^2)不好。

所有顶层都需要类型签名

在编写快速而肮脏的代码时,这很烦人。对于简单的代码,最多使用一种或两种数据类型(例如,我知道我只使用Doubles),在任何地方编写类型签名可能会使阅读变得复杂。在上面的示例中,仅针对缺少类型签名有两个警告:

rt.hs:1:0:
    Warning: Definition but no type signature for `norm2'
             Inferred type: norm2 :: forall a. (Floating a) => a -> a -> a
...

rt.hs:2:15:
    Warning: Defaulting the following constraint(s) to type `Double'
             `Floating a' arising from a use of `norm2' at rt.hs:2:15-23
    In the second argument of `($)', namely `norm2 1 1'
    In the expression: print $ norm2 1 1
    In the definition of `main': main = print $ norm2 1 1

作为一种干扰,其中之一是指与需要类型签名的行不同的行。

中间计算的类型签名Integral是必要的

这是第一个问题的一般情况。考虑一个例子:

stripe x = fromIntegral . round $ x - (fromIntegral (floor x))
main = mapM_ (print . stripe) [0,0.1..2.0]

它给出了一堆警告。到处fromIntegral都可以转换回Double

rt2.hs:1:11:
    Warning: Defaulting the following constraint(s) to type `Integer'
             `Integral b' arising from a use of `fromIntegral' at rt2.hs:1:11-22
    In the first argument of `(.)', namely `fromIntegral'
    In the first argument of `($)', namely `fromIntegral . round'
    In the expression:
            fromIntegral . round $ x - (fromIntegral (floor x))

fromIntegral每个人都知道在 Haskell 中需要多久......


像这样的情况更多,数字代码可能会因为满足-Wall要求而变得不可读。但我仍然使用-Wall我想确定的代码。

于 2010-11-15T12:12:05.367 回答
7

我建议继续使用“-Wall”作为默认选项,并使用相关文件顶部的 OPTIONS_GHC pragma 禁用您需要在本地、每个模块基础上进行的任何检查。

我可能会例外的一个确实是'-fno-warn-unused-do-bind',但一个建议可能是使用明确的'void'函数......写'void f'似乎比'_ < - F'。

至于名称阴影——我认为如果可以的话,通常最好避免——在一些代码中间看到“map”会导致大多数 Haskellers 期望标准库 fn。

于 2010-11-14T07:22:10.813 回答
6

名称隐藏可能非常危险。特别是,很难推断名称被引入的范围。

do 表示法中未使用的模式绑定并没有那么糟糕,但可以表明正在使用效率低于必要的函数(例如mapM,而不是mapM_)。

正如 BenMos 指出的那样,使用voidignore显式丢弃未使用的值是一种明确事物的好方法。

如果能够只对一段代码禁用警告,而不是同时对所有代码禁用警告,那就太好了。此外,cabal 标志和命令行 ghc 标志优先于文件中的标志,因此我不能在任何地方都默认使用 -Wall ,甚至可以轻松地为整个单个文件禁用它。

于 2010-11-14T19:41:39.330 回答
4

所有这些警告都有助于防止错误,应该受到尊重,而不是压制。如果要使用 Prelude 中的名称定义函数,可以使用隐藏它

导入前奏隐藏(图)

“隐藏”语法只能用于 Prelude 和同一个包的模块,否则您将面临因导入模块中的 API 更改而导致代码损坏的风险。

见:http ://www.haskell.org/haskellwiki/Import_modules_properly

于 2010-11-21T17:42:26.897 回答
4

还有一个侵入性小得多的-W选项,它启用了一组合理的警告,这些警告主要与一般编码风格相关(未使用的导入、未使用的变量、不完整的模式匹配等)。

特别是它不包括您提到的两个警告。

于 2012-03-04T05:33:41.060 回答