It is considered good practice to enable GHC warnings with -Wall
. However, I've found out that fixing those warnings has a negative effect for some types of code constructs.
Example 1:
Using the do-notation equivalent of f >>
will generate a warning if I don't explicitly use the _ <- f
form:
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
I understand that I can forget to do something with the result of f
. However, it is legitimate to ignore the result (very common in parsers). There is no warning when using >>
, right? Using _ <-
is heavier than it should.
Example 2:
Naming a pattern variable with the same name of a visible function will give:
Warning: This binding for `map' shadows the existing binding imported from Prelude
This is getting worse when using record syntax as namespace gets polluted quickly. The solution is to give an alternate name in the pattern expression. So I end up using a less appropriate name just to avoid a warning. I don't feel it's a good-enough reason.
I know I can use -fno-warn-...
options but should I stick with -Wall
after all?
Example 1:
I have re-learned to write parsers in Applicative style -- they are much more concise. Eg, instead of:
funCallExpr :: Parser AST funCallExpr = do func <- atom token "(" arg <- expr token ")" return $ FunCall func arg
I instead write:
funCallExpr :: Parser AST funCallExpr = FunCall <$> atom <* token "(" <*> expr <* token ")"
But what can I say, if you don't like the warning, disable it as it suggests.
Example 2:
Yeah I find that warning a bit irritating as well. But it has saved me a couple times.
It ties into naming conventions. I like to keep modules pretty small, and keep most imports qualified (except for "notation" imports like Control.Applicative
and Control.Arrow
). That keeps the chances of name conflict low, and it just makes things easy to work with. hothasktags
makes this style tolerable if you are using tags.
If you are just pattern matching on a field with the same name, you can use -XNamedFieldPuns
or -XRecordWildCards
to reuse the name:
data Foo = Foo { baz :: Int, bar :: String } -- RecordWildCards doubleBaz :: Foo -> Int doubleBaz (Foo {..}) = baz*baz -- NamedFieldPuns reverseBar :: Foo -> String reverseBar (Foo {bar}) = reverse bar
Another common convention is to add a hungarian prefix to record labels:
data Foo = Foo { fooBaz :: Int, fooBar :: String }
But yeah, records are no fun to work with in Haskell. Anyway, keep your modules small and your abstractions tight and this shouldn't be a problem. Consider it as a warning that says simplifyyyy, man.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With