Impact on style of GHC -Wall - haskell

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.

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 exponents
Consider this code:
norm2 x y = sqrt (x^2 + y^2)
main = print $ norm2 1 1
With -Wall it gives two warnings like this:
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)
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 Doubles), writing type signatures everywhere may complicate reading. In the example above there are two warnings just for the lack of type signature:
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
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 necessary
This is a general case of the first problem. Consider an example:
stripe x = fromIntegral . round $ x - (fromIntegral (floor x))
main = mapM_ (print . stripe) [0,0.1..2.0]
It gives a bunch of warnings. Everywhere with fromIntegral to convert back to 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))
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.

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.

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 of mapM_).
As BenMos pointed out, using void or ignore 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.

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

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.

Related

Hide GHC base library to prevent pattern matching desugaring to GHC.Num.fromInteger use

I have created a replacement Prelude for use in teaching beginning Haskell students, called FirstPrelude. One of the aims is to expunge type classes from the standard library so that error messages are more of the classic Hindley-Milner variety, rather than getting No instance errors. This is working out quite well. However, something I did not anticipate is that, when pattern matching, GHC is side-stepping my redefinition of fromInteger (defined as the identity, monomorphised to only work on Integer) and so for example, with this function:
isZero 0 = True
isZero _ = False
If I ask GHCi for the type, I get:
isZero :: (GHC.Classes.Eq a, GHC.Num.Num a) => a -> Bool
But what I want is to get Integer -> Bool. Dumping the simplified core out of GHC I can see it is using:
(GHC.Num.fromInteger #Integer GHC.Num.$fNumInteger 0)))
I would have thought it would just use my fromInteger :: Integer -> Integer that is in scope, but alas no. Is there a way I can somehow prevent GHC.Num.fromInteger from being used? I guess perhaps this possible at the package level, but really I would love this at the module level for other pedagogical reasons.
This is not tied to pattern matching: In your Example.hs, even literals in expressions are polymorphic. To see this, write something like
simpler :: ()
simpler = 1
and observe the error message
[2 of 2] Compiling Main ( Example.hs, interpreted )
Example.hs:7:11: error:
• No instance for (GHC.Num.Num ()) arising from the literal ‘1’
• In the expression: 1
In an equation for ‘simpler’: simpler = 1
|
7 | simpler = 1
| ^
Failed, one module loaded.
You may not have noticed because as soon as you use one of “your” operations, GHC specializes the type, and for top-level values may use defaulting.
But note
simpler _ = 1
for which it infers this type
ghci> :t simpler
simpler :: GHC.Num.Num p1 => p2 -> p1
If you enable {-# LANGUAGE RebindableSyntax #-}, it really uses “your” fromInteger and things work as you expect. You can do that in the .cabal file, maybe good enough for your educational needs.

Why such different behaviour with `Ambiguous type..` error (in ghci)?

This example works with ghci, load this file:
import Safe
t1 = tailMay []
and put in ghci:
> print t1
Nothing
But if we add analogous definition to previous file, it doesn't work:
import Safe
t1 = tailMay []
t2 = print $ tailMay []
with such error:
* Ambiguous type variable `a0' arising from a use of `print'
prevents the constraint `(Show a0)' from being solved.
Probable fix: use a type annotation to specify what `a0' should be.
These potential instances exist:
instance Show Ordering -- Defined in `GHC.Show'
instance Show Integer -- Defined in `GHC.Show'
instance Show a => Show (Maybe a) -- Defined in `GHC.Show'
...plus 22 others
That is 3rd sample for ghc with the same error:
import Safe
t1 = tailMay
main = do
print $ t1 []
print $ t1 [1,2,3]
Why? And how to fix the second sample without explicit type annotation?
The issue here is that tailMay [] can generate an output of type Maybe [a] for any a, while print can take an input of type Maybe [a] for any a (in class Show).
When you compose a "universal producer" and a "universal consumer", the compiler has no idea about which type a to pick -- that could be any type in class Show. The choice of a could matter since, in principle, print (Nothing :: Maybe [Int]) could print something different from print (Nothing :: Maybe [Bool]). In this case, the printed output would be the same, but only because we are lucky.
For instance print ([] :: [Int]) and print ([] :: [Char]) will print different messages, so print [] is ambiguous. Hence, GHC reject it, and requires an explicit type annotation (or a type application # type, using an extension).
Why, then, such ambiguity is accepted in GHCi? Well, GHCi is meant to be used for quick experiments, and as such, as a convenience feature, it will try hard to default these ambiguous a. This is done using the extended defaulting rules, which could (I guess) in principle be turned on in GHC as well by turning on that extension.
This is, however, not recommended since sometimes the defaulting rule can choose some unintended type, making the code compile but with an unwanted runtime behavior.
The common solution to this issue is using an annotation (or # type), because it provides more control to the programmer, makes the code easier to read, and avoids surprises.

How to work around issue with ambiguity when monomorphic restriction turned *on*?

So, learning Haskell, I came across the dreaded monomorphic restriction, soon enough, with the following (in ghci):
Prelude> let f = print.show
Prelude> f 5
<interactive>:3:3:
No instance for (Num ()) arising from the literal `5'
Possible fix: add an instance declaration for (Num ())
In the first argument of `f', namely `5'
In the expression: f 5
In an equation for `it': it = f 5
So there's a bunch of material about this, e.g. here, and it is not so hard to workaround.
I can either add an explicit type signature for f, or I can turn off the monomorphic restriction (with ":set -XNoMonomorphismRestriction" directly in ghci, or in a .ghci file).
There's some discussion about the monomorphic restriction, but it seems like the general advice is that it is ok to turn this off (and I was told that this is actually off by default in newer versions of ghci).
So I turned this off.
But then I came across another issue:
Prelude> :set -XNoMonomorphismRestriction
Prelude> let (a,g) = System.Random.random (System.Random.mkStdGen 4) in a :: Int
<interactive>:4:5:
No instance for (System.Random.Random t0)
arising from the ambiguity check for `g'
The type variable `t0' is ambiguous
Possible fix: add a type signature that fixes these type variable(s)
Note: there are several potential instances:
instance System.Random.Random Bool -- Defined in `System.Random'
instance System.Random.Random Foreign.C.Types.CChar
-- Defined in `System.Random'
instance System.Random.Random Foreign.C.Types.CDouble
-- Defined in `System.Random'
...plus 33 others
When checking that `g' has the inferred type `System.Random.StdGen'
Probable cause: the inferred type is ambiguous
In the expression:
let (a, g) = System.Random.random (System.Random.mkStdGen 4)
in a :: Int
In an equation for `it':
it
= let (a, g) = System.Random.random (System.Random.mkStdGen 4)
in a :: Int
This is actually simplified from example code in the 'Real World Haskell' book, which wasn't working for me, and which you can find on this page: http://book.realworldhaskell.org/read/monads.html (it's the Monads chapter, and the getRandom example function, search for 'getRandom' on that page).
If I leave the monomorphic restriction on (or turn it on) then the code works. It also works (with the monomorphic restriction on) if I change it to:
Prelude> let (a,_) = System.Random.random (System.Random.mkStdGen 4) in a :: Int
-106546976
or if I specify the type of 'a' earlier:
Prelude> let (a::Int,g) = System.Random.random (System.Random.mkStdGen 4) in a :: Int
-106546976
but, for this second workaround, I have to turn on the 'scoped type variables' extension (with ":set -XScopedTypeVariables").
The problem is that in this case (problems when monomorphic restriction on) neither of the workarounds seem generally applicable.
For example, maybe I want to write a function that does something like this and works with arbitrary (or multiple) types, and of course in this case I most probably do want to hold on to the new generator state (in 'g').
The question is then: How do I work around this kind of issue, in general, and without specifying the exact type directly?
And, it would also be great (as a Haskell novice) to get more of an idea about exactly what is going on here, and why these issues occur..
When you define
(a,g) = random (mkStdGen 4)
then even if g itself is always of type StdGen, the value of g depends on the type of a, because different types can differ in how much they use the random number generator.
Moreover, when you (hypothetically) use g later, as long as a was polymorphic originally, there is no way to decide which type of a you want to use for calculating g.
So, taken alone, as a polymorphic definition, the above has to be disallowed because g actually is extremely ambiguous and this ambiguity cannot be fixed at the use site.
This is a general kind of problem with let/where bindings that bind several variables in a pattern, and is probably the reason why the ordinary monomorphism restriction treats them even stricter than single variable equations: With a pattern, you cannot even disable the MR by giving a polymorphic type signature.
When you use _ instead, presumably GHC doesn't worry about this ambiguity as long as it doesn't affect the calculation of a. Possibly it could have detected that g is unused in the former version, and treated it similarly, but apparently it doesn't.
As for workarounds without giving unnecessary explicit types, you might instead try replacing let/where by one of the binding methods in Haskell which are always monomorphic. The following all work:
case random (mkStdGen 4) of
(a,g) -> a :: Int
(\(a,g) -> a :: Int) (random (mkStdGen 4))
do (a,g) <- return $ random (mkStdGen 4)
return (a :: Int) -- The result here gets wrapped in the Monad

Weird behaviour GHCi Haskell Compiler

In a test I'm asked to infer the type of:
let pr = map head.group.sortBy(flip compare)
I've concluded after inferring it myself that the type was:
Ord a => [a] -> [a]
However when doing :t in GHCi it says the type is:
pr :: [()] -> [()]
What is going on?
Also if in GHCi I do:
map head.group.sortBy(flip compare) [1,2,3,4,100,50,30,25,51,70,61]
I get an error:
Couldn't match expected type `a0 -> [b0]' with actual type `[a1]'
In the return type of a call of `sortBy'
Probable cause: `sortBy' is applied to too many arguments
In the second argument of `(.)', namely
`sortBy (flip compare) [1, 2, 3, 4, ....]'
In the second argument of `(.)', namely
`group . sortBy (flip compare) [1, 2, 3, 4, ....]'
However if I do:
sortBy(flip compare) [1,2,3,4,100,50,30,25,51,70,61]
[100,70,61,51,50,30,25,4,3,2,1]
It works just fine. Why is the first expression failing when the second evaluates sortBy just fine with the exact same arguments?
Your first problem is the dreaded combination of the Monomorphism Restriction, GHCi's inability to see your whole program at once, and GHCi's extended defaulting rules.
In a nutshell, Haskell doesn't like to infer types with polymorphic type class constraints (the Ord a => part of your type signature) for top-level bindings that are written as equations that syntactically do not have arguments. pr = map head.group.sortBy(flip compare) falls foul of this rule (it's a function, so semantically it has arguments, but the equation you're using to define it doesn't), so Haskell wants the Ord-constrained a to be something concrete.
If you put this in a source file and compile it (even via GHCi):
import Data.List
pr = map head.group.sortBy(flip compare)
You get outright errors, like:
foo.hs:3:33:
No instance for (Ord b0) arising from a use of `compare'
The type variable `b0' is ambiguous
Possible cause: the monomorphism restriction applied to the following:
pr :: [b0] -> [b0] (bound at foo.hs:3:1)
Probable fix: give these definition(s) an explicit type signature
or use -XNoMonomorphismRestriction
Note: there are several potential instances:
instance Integral a => Ord (GHC.Real.Ratio a)
-- Defined in `GHC.Real'
instance Ord () -- Defined in `GHC.Classes'
instance (Ord a, Ord b) => Ord (a, b) -- Defined in `GHC.Classes'
...plus 22 others
In the first argument of `flip', namely `compare'
In the first argument of `sortBy', namely `(flip compare)'
In the second argument of `(.)', namely `sortBy (flip compare)'
Failed, modules loaded: none.
For some types in particular (notably numeric types) this kind of "ambiguous type variable" error comes up a lot and would be irritating, so Haskell has some defaulting rules. For example, it will assume an ambiguous type variable constrained only by Num should be Integer. Of course, if you use the function anywhere in the same file like so:
import Data.List
pr = map head.group.sortBy(flip compare)
answer = pr [1,2,3,4,100,50,30,25,51,70,61]
then Haskell can take into account. It still refuses to infer a polymorphic type for pr, but in this case pr is only ever used as if it were [Integer] -> [Integer], so it'll give it that type and allow your code to compile, rather than issue the ambiguous type variable error (the Integer itself is also a result of type defaulting).
In GHCi, your code is compiled one statement at a time, so it can't take into account your use of pr to decide what type to give it. It would give you an ambiguous type error except that GHCi has extended defaulting rules, which kick in here to "save the day" and allow your expression to compile. By defaulting the Ord a => a type variable to the unit type (), your declaration can be interpreted as the definition of a function for condensing arbitrary lists of () into [()] (or [] if the input was empty). Thanks GHCi!
You can resolve this in a few of different ways. One is to add an argument to both sides of your definition of pr, like so:
let pr z = map head.group.sortBy(flip compare) $ z
Now the equation defining pr has an argument syntacically (it's type/meaning still has the same number of arguments), the Monomorphism Restriction doesn't kick in, and Haskell is happy to infer a polymorphic type for pr.
Another is to explicitly tell it you don't want to use the Monomorphism Restriction by either adding {-# LANGUAGE NoMonomorphismRestriction #-} to the top of your module, or by using :set -XNomonomorphismRestriction at the GHCi prompt. Then it will again infer the type Ord a => [a] -> [a] for pr.
A third way is to explicitly give the polymorphic type signature for your function:
import Data.List
pr :: Ord a => [a] -> [a]
pr = map head.group.sortBy(flip compare)
Or in GHCi:
> let { pr :: Ord a => [a] -> [a] ; pr = map head.group.sortBy(flip compare) }
Since even with the Monomorphism Restriction in force Haskell is happy for pr to have a polymorphic type, it just won't infer one for it.
The explicit type signature is probably the most common way people avoid this problem in compiled files, because many people consider it good style to always provide type signatures for top level definitions. In GHCi it's pretty annoying, as you can see; I usually turn off the Monomorphism Restriction there.
As for your second problem, I'm afraid this:
map head.group.sortBy(flip compare) [1,2,3,4,100,50,30,25,51,70,61]
is very different from this:
pr [1,2,3,4,100,50,30,25,51,70,61]
When you've got pr defined as a function, pr refers to the whole function map head.group.sortBy(flip compare), so feeding it an argument feeds an argument to that function. But when you write out the whole expression, just sticking a list to the right of it does not pass it as an argument to the whole expression. It's parsed a bit more like this:
(map head) . (group) . (sortBy (flip compare) [1,2,3,4,100,50,30,25,51,70,61])
As you can see, the list is inside the last function in the pipeline; sortBy (flip compare) [1,2,3,4,100,50,30,25,51,70,61] is being used as a function, which will take an argument and feed its output further through the pipeline (to group). That clearly doesn't make sense, and is why you get an error message complaining about too many arguments being given to sortBy; it's not that you have provided too many arguments to sortBy, but rather that you've provided all its arguments and then used it in a position where it would have to be able to take one more.
This can sometimes be surprising until you get used to it, but any alternative is surprising more frequently (you implicitly depended on parsing working this way in your use of map head and sortBy (flip compare)). All you need to do is remember that ordinary function application (by just sticking two expressions next to each other) is always higher precedence than infix operators (like .); whenever you've got an expression mixing infix operators and ordinary application, each normal application chain (groups of non-operator expressions separated only by whitespace) becomes only a single argument as far as the infix operators are concerned (and then precedence/associativity is used to resolve what the arguments of the infix operators are).
To fix it, you need to add parentheses around the composition pipeline before you introduce the argument, like so:
(map head.group.sortBy(flip compare)) [1,2,3,4,100,50,30,25,51,70,61]
Or use $ to put a "wall" between the composition pipeline and the argument, like so:
map head.group.sortBy(flip compare) $ [1,2,3,4,100,50,30,25,51,70,61]
This works because $ is another infix operator, so it forces all the "normal application" sequences to its left and right to be resolved before one can be applied to the other. It's also a very low precedence operator, so it almost always works when there are other infix operators in play as well (like the .). It's quite a common idiom in Haskell to write expressions of the form f . g . h $ a.
You've been bitten by defaulting, where GHCi (interactive GHCi, not GHC compiling something) will put () in any uninstantiated type parameter in certain cases.
I think you've mixed up . and $. Consider your original expression:
map head . group . sortBy(flip compare) [1,2,3,4,100,50,30,25,51,70,61]
That composes the functions map head, group, and sortBy (flip compare) [...]. Unfortunately, sortBy (flip compare) [...] is a list, not a function, so it can't be composed like that. sortBy (flip compare), however, is, and if we compose those functions together and then apply that function to the list, it'll work:
map head . group . sortBy (flip compare) $ [1,2,3,4,100,50,30,25,51,70,61]

Why can't I use record selectors with an existentially quantified type?

When using Existential types, we have to use a pattern-matching syntax for extracting the foralled value. We can't use the ordinary record selectors as functions. GHC reports an error and suggest using pattern-matching with this definition of yALL:
{-# LANGUAGE ExistentialQuantification #-}
data ALL = forall a. Show a => ALL { theA :: a }
-- data ok
xALL :: ALL -> String
xALL (ALL a) = show a
-- pattern matching ok
-- ABOVE: heaven
-- BELOW: hell
yALL :: ALL -> String
yALL all = show $ theA all
-- record selector failed
forall.hs:11:19:
Cannot use record selector `theA' as a function due to escaped type variables
Probable fix: use pattern-matching syntax instead
In the second argument of `($)', namely `theA all'
In the expression: show $ theA all
In an equation for `yALL': yALL all = show $ theA all
Some of my data take more than 5 elements. It's hard to maintain the code if I
use pattern-matching:
func1 (BigData _ _ _ _ elemx _ _) = func2 elemx
Is there a good method to make code like that maintainable or to wrap it up so that I can use some kind of selectors?
Existential types work in a more elaborate manner than regular types. GHC is (rightly) forbidding you from using theA as a function. But imagine there was no such prohibition. What type would that function have? It would have to be something like this:
-- Not a real type signature!
theA :: ALL -> t -- for a fresh type t on each use of theA; t is an instance of Show
To put it very crudely, forall makes GHC "forget" the type of the constructor's arguments; all that the type system knows is that this type is an instance of Show. So when you try to extract the value of the constructor's argument, there is no way to recover the original type.
What GHC does, behind the scenes, is what the comment to the fake type signature above says—each time you pattern match against the ALL constructor, the variable bound to the constructor's value is assigned a unique type that's guaranteed to be different from every other type. Take for example this code:
case ALL "foo" of
ALL x -> show x
The variable x gets a unique type that is distinct from every other type in the program and cannot be matched with any type variable. These unique types are not allowed to escape to the top level—which is the reason why theA cannot be used as a function.
You can use record syntax in pattern matching,
func1 BigData{ someField = elemx } = func2 elemx
works and is much less typing for huge types.

Resources