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?
All these warnings help to prevent mistakes and should be respected, not suppressed. If you want to define a function with a name from Prelude, you can hide it using
import Prelude hiding (map)
'Hiding' syntax should only be used for Prelude and modules of the same package, otherwise you risk code breakage by API changes in the imported module.
See: http://www.haskell.org/haskellwiki/Import_modules_properly
I would recommend continuing to use '-Wall' as the default option, and disable any checks you need to on local, per-module basis using an OPTIONS_GHC pragma at the top of relevant files.
The one I might make an exception for is indeed '-fno-warn-unused-do-bind', but one suggestion might be to use an explicit 'void' function ... writing 'void f' seems nicer than '_ <- f'.
As for name shadowing - I think it's generally good to avoid if you can - seeing 'map' in the middle of some code will lead most Haskellers to expect the standard library fn.
Example 1:
I have re-learned to write parsers in Applicative style -- they are much more concise. Eg, instead of:
I instead write:
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
andControl.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:Another common convention is to add a hungarian prefix to record labels:
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.
I think that use of
-Wall
may lead to less readable code. Especially, if it is doing some arithmetics.Some other examples, where the use of
-Wall
suggests modifications with worse readability.(^)
with-Wall
requires type signatures for exponentsConsider this code:
With
-Wall
it gives two warnings like this:Writing
(^(2::Int)
everywhere instead of(^2)
is not nice.Type signatures are required for all top-levels
When writing quick and dirty code, it's annoying. For simple code, where there are at most one or two data types in use (for exapmle, I know that I work only with
Double
s), writing type signatures everywhere may complicate reading. In the example above there are two warnings just for the lack of type signature:As a distraction, one of them refers to the line different from the one where the type signature is needed.
Type signatures for intermediate calculations with
Integral
are necessaryThis is a general case of the first problem. Consider an example:
It gives a bunch of warnings. Everywhere with
fromIntegral
to convert back toDouble
:And everyone knows how often one needs
fromIntegral
in Haskell...There are more cases like these the numeric code risks to become unreadable just to fulfill the
-Wall
requirements. But I still use-Wall
on the code I'd like to be sure of.There is also the much less intrusive
-W
option, which enables a set of reasonable warnings mostly related to general coding style (unused imports, unused variables, incomplete pattern matches, etc.).In particular it does not include the two warnings you mentioned.
Name shadowing can be quite dangerous. In particular, it can become difficult to reason about what scope a name is introduced in.
Unused pattern binds in do notation are not as bad, but can indicate that a less efficient function than necessary is being used (e.g.
mapM
instead ofmapM_
).As BenMos pointed out, using
void
orignore
to explicitly discard unused values is a nice way to be explicit about things.It would be quite nice to be able to disable warnings for just a section of code, rather than for everything at once. Also, cabal flags and command line ghc flags take precedence over flags in a file, so I can't have -Wall by default everywhere and even easily just disable it for the entirety of a single file.