If I have an expression that I know has an ambiguous type, is there a way to get GHCi to actually tell me that type in full, so I can see the exact ambiguity for myself, instead of having to piece it together from bits of the error messages? Example:
GHCi, version 9.0.1: https://www.haskell.org/ghc/ :? for help
ghci> default ()
ghci> :t v show . read
<interactive>:1:1: error:
• Ambiguous type variable ‘b0’ arising from a use of ‘show’
prevents the constraint ‘(Show b0)’ from being solved.
Probable fix: use a type annotation to specify what ‘b0’ should be.
These potential instances exist:
instance Show Ordering -- Defined in ‘GHC.Show’
instance Show a => Show (Maybe a) -- Defined in ‘GHC.Show’
instance Show Integer -- Defined in ‘GHC.Show’
...plus 23 others
...plus 21 instances involving out-of-scope types
(use -fprint-potential-instances to see them all)
• In the first argument of ‘(.)’, namely ‘show’
In the expression: show . read
In the expression: show . read
<interactive>:1:8: error:
• Ambiguous type variable ‘b0’ arising from a use of ‘read’
prevents the constraint ‘(Read b0)’ from being solved.
Probable fix: use a type annotation to specify what ‘b0’ should be.
These potential instances exist:
instance Read Ordering -- Defined in ‘GHC.Read’
instance Read a => Read (Maybe a) -- Defined in ‘GHC.Read’
instance Read Integer -- Defined in ‘GHC.Read’
...plus 23 others
...plus 10 instances involving out-of-scope types
(use -fprint-potential-instances to see them all)
• In the second argument of ‘(.)’, namely ‘read’
In the expression: show . read
In the expression: show . read
ghci>
In that example, what I want is for GHCi to tell me show . read :: (Show a, Read a) => String -> String
or something like that.
CodePudding user response:
You won't get ghci to tell you this ambiguous type, but don't worry. This show . read
example is rather artificial and you won't run into it in practice. You can at least make a definition out of it
readShow :: forall a. Read a => Show a => String -> String
readShow = show @a . read @a
CodePudding user response:
GHCi is giving you the information you want, just not in the format you want.
With -fdefer-type-errors
you get show . read :: String -> String
after the errors. You're disappointed that it's not something like show . read :: (Read a, Show a) => String -> String
.
The trouble is that the whole reason it is ambiguous is that the type variable a
doesn't appear in the type of the overall expression, in order for the constraints to be attached. The constraints have to be solved "internally" to the expression, having nothing to do with the overall type String -> String
.
GHC could of course add a spurious type variable to the type anyway, and say show . read :: (Read a, Show a) => String -> String
just to tell you about the ambiguous type. But it already tells you about the ambiguous type, and if you actually read the error messages it tells you:
• Ambiguous type variable ‘a0’ arising from a use of ‘show’
prevents the constraint ‘(Show a0)’ from being solved.
Exactly what the problem is
Probable fix: use a type annotation to specify what ‘a0’ should be.
That to fix it, you probably have to add more type information
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
...plus 21 instances involving out-of-scope type
A list of "potential" instances telling you what types you might be able to use in that additional type information it suggested you might need (this isn't very useful here, admittedly).
• In the first argument of ‘(.)’, namely ‘show’
In the expression: show . read
Exactly where the problem is; pinpointing the exact sub-expression (because there could conceivably be more than one use of show
on the same line).
show
is the exact point in the code where a Show
instance is needed and there is ambiguity about what type it should be using to find an instance. Not the return type of the call to .
, which is unambiguously String -> String
. So that's what GHC tells you (and similarly for the Read
constraint in the next error message).
Consider this example:
ghci> :t length [1] 10
length [1] 10 :: Int
Under normal defaulting rules, that's fine. With default ()
you get an ambiguous type error about being unable to solve the constraint Num a0
. But is seeing the following type helpful?
length [1] 10 :: (Num a) => Int
How do I know where the ambiguity is from that? Is it the 1
or the 10
, or does length
maybe need a Num
constraint (it could have been written like genericLength
)? What GHC actually tells you is1:
• Ambiguous type variable ‘a0’ arising from the literal ‘1’
prevents the constraint ‘(Num a0)’ from being solved
Which is more informative than what you want.
You say you want to "see the exact ambiguity for myself, instead of having to piece it together from bits of the error messages", but that's actually not what you're asking for. GHC is telling you the exact ambiguity (both the constraint that can't be solved and where it needs to be solved). What you are asking for is to see a type signature with variables in the constraints that don't appear on the right hand side of the =>
, so that you can infer what must have gone wrong in the internals to cause that instead of reading the error message where it tells you what went wrong in the internals to cause that.
Presumably you want that because it would be much shorter and easier for you to recognise, which I can certainly understand! I think the right way to deal with that problem though is just to learn GHC's error messages until you can "pattern match" Ambiguous type variable ‘a0’ arising from a use of ‘read’ prevents the constraint ‘(Read a0)’ from being solved.
just as easily as you can (Read a, Show a) => String -> String
.
1 In fact the return type of length
is unambiguously Int
, which sets the Num
constraint used by
and 10
to Int
, so the overall expression is unambiguously typed Int
. The ambiguity is solely in the type of the 1
inside the list, not in the return type of
, so there should not be a Num
constraint in the result.
CodePudding user response:
I don't think you can get GHCI to tell you. I think this is because even if you turn on, say, AllowAmbiguoustypes so that the expression compiles, GHCI's defaulting rules resolve the the readable type as ()
, rather than as a type parameter. There's no more interesting type left to show than String -> String
. And there's no point leaving it polymorphic, because there's no place a client of this function could possibly put a type annotation to indicate what type they want to read as.
Contrast this with read . show
, which GHCI will happily give you the type for because it can infer the type to use based on the arguments given.