MonoLocalBinds
is a bugbear of mine, so here’s my take on it.
GADTs
implies MonoLocalBinds
(to give predictable type inference). That in turn can make some, relatively obscure, program fail.
I wouldn’t call the all the programs that fail when MonoLocalBinds
is enabled “relatively obscure”. Here’s a simple one:
f :: Bool -> ReadP (Char, String)
f expectWhitespace =
(,) <$> g get (char '\n') <*> g look (string "\n\n")
where
g x y = if expectWhitespace then x <* y else x
There are a couple of problems that make the problem with MonoLocalBinds
particularly acute:
- The error message doesn’t give any clue that
MonoLocalBinds
is the problem, and that adding a fully general type signature is the required solution. In Haskell we’re used to local definitions having their types generalised (notwithstanding some paper titles to the contrary)!
- Even if I did know that, the fully general type siganture can be quite long and require importing additional types to even write at all (here it’s just
g :: ReadP a -> ReadP b -> ReadP a
so it’s not terrible but not great).
The error message:
test18.hs:7:33: error: [GHC-83865]
• Couldn't match type ‘Char’ with ‘[Char]’
Expected: ReadP String
Actual: ReadP Char
• In the second argument of ‘(<*>)’, namely
‘g look (string "\n\n")’
In the expression:
(,) <$> g get (char '\n') <*> g look (string "\n\n")
In an equation for ‘f’:
f expectWhitespace
= (,) <$> g get (char '\n') <*> g look (string "\n\n")
where
g x y = if expectWhitespace then x <* y else x
|
7 | (,) <$> g get (char '\n') <*> g look (string "\n\n")
| ^^^^^^^^^^^^^^^^^^^^^^
test18.hs:7:35: error: [GHC-83865]
• Couldn't match type ‘[Char]’ with ‘Char’
Expected: ReadP Char
Actual: ReadP String
• In the first argument of ‘g’, namely ‘look’
In the second argument of ‘(<*>)’, namely ‘g look (string "\n\n")’
In the expression:
(,) <$> g get (char '\n') <*> g look (string "\n\n")
|
7 | (,) <$> g get (char '\n') <*> g look (string "\n\n")
| ^^^^
test18.hs:7:41: error: [GHC-83865]
• Couldn't match type ‘[Char]’ with ‘Char’
Expected: ReadP Char
Actual: ReadP String
• In the second argument of ‘g’, namely ‘(string "\n\n")’
In the second argument of ‘(<*>)’, namely ‘g look (string "\n\n")’
In the expression:
(,) <$> g get (char '\n') <*> g look (string "\n\n")
|
7 | (,) <$> g get (char '\n') <*> g look (string "\n\n")
| ^^^^^^^^^^^^^