r/haskell • u/gbelloz • 1d ago
An exercise in interpreting compiler errors
I decided to use this as an exercise to get better at interpretting GHC's error messages.
I know that the error here is that I should use (+) because it's an infix operator.
But, ignoring that, how do I understand this error, and, bonus, how would I use that understanding to know what the error actually was?
(1) ghci> (+ (5 :: Integer) (6 :: Integer))
(2) <interactive>:93:4: error:
(3) • Couldn't match expected type ‘Integer -> a’
(4) with actual type ‘Integer’
(5) • The function ‘5 :: Integer’ is applied to one value argument,
(6) but its type ‘Integer’ has none
(7) In the expression: (5 :: Integer) (6 :: Integer)
(8) In the expression: + (5 :: Integer) (6 :: Integer)
(9) • Relevant bindings include
(10) it :: a -> a (bound at <interactive>:93:1)
Here's what I know:
Line (3) says the type that type unification makes it expect.
Line (4) is the type of the value that I'm trying to apply.
Line (5) clearly says it's interpretting (5 :: Integer) as a function. Why?
Line (6) dunno
Line (7) the expression where all this happened
Line (8) working out from the function nesting
Line (10) dunno
What am I missing?
5
u/tdammers 1d ago
Here's how I would dissect that error message:
First, it says "Couldn't match expected type...", which means that GHC thinks there is a type error - you're using something of a certain type in a context that suggests a different, incompatible type. The types in question are Integer -> a (what the context suggests), and Integer (what was used in that context).
OK, so what is the context, and what is the thing that was used in the context? That's what the next bullet point tells us: "The function 5 :: Integer" is the thing that was used in the context, and "is applied to one value argument" is the context. It also tells us why the thing doesn't fit into the context: "but its type 'Integer' has none". In other words, this says that you tried to use 5 :: Integer as a one-argument function, but because its type Integer is not a function type, this doesn't typecheck.
Great, so this means that either the context is wrong, or the thing you put into it. Most commonly, the thing you put in is wrong, but in this case, it doesn't look like it - 5 is indeed an integer, and explicitly fixing its type to Integer can't be the problem. However, using 5 as a function doesn't make sense, so we need to figure out why GHC thinks you're trying to do just that. The next lines give us some hints: "In the expression: (5 :: Integer) (6 :: Integer)" means that GHC considers that part a (sub-)expression, and because juxtaposition (function, space, argument) is Haskell's syntax for function application, this expression means "apply the function (5 :: Integer) to the argument (6 :: Integer)".
That's not what you meant, and it may not be immediately obvious why GHC parses the code that way - you might think that (+ (5 :: Integer) (6 :: Integer)) parses the same way as (add (5 :: Integer) (6 :: Integer)) might: applying the + (or add) function to two arguments, namely (5 :: Integer) and (6 :: Integer). But according to this error message, that's not how it's parsed; instead, it parses it as (+ ((5 :: Integer) (6 :: Integer)), that is, an operator section that desugars into (\x -> x + ((5 :: Integer) (6 :: Integer)), just like (+ 5) desugars into (\x -> x + 5), and (+ negate 5) desugars into (\x -> x + negate 5).
Lines 9 and 10 give you "relevant bindings", that is, the types of some identifiers that were in scope at the site of the error, and that GHC thinks may be relevant to the error. Normally, this will include some useful things, but in this case, the only thing GHC finds worth reporting is the entire expression you typed into GHCi, which is internally represented as it (and "bound at <interactive>" means that it was defined (a.k.a. "bound") in your interactive session, rather than some Haskell source file). What GHC tells you here is the type it has inferred for that expression so far: a -> a. What this means is that it has inferred that your expression should evaluate to some function that takes a single argument and returns something of the same type - which is correct, because (+ x) means "a function that takes an argument and adds x to it", and the type of such a function is a -> a, where a is whatever the type of x is. But because GHC encountered a type error while inferring the type of x, it doesn't get any further than a -> a. This information isn't necessary to figure out the reason for the type error in this case, but it can be useful sometimes.
2
u/jeffstyr 1d ago
Just as a side comment, I think error messages are so often difficult to decypher because: The message can't tell you exactly what needs fixing because almost always there are multiple choices for what the correct code is that you were trying to write (e.g., if there is a type mismatch between a function and the argument you are supplying, the mistake could be: you supplied the wrong argument, you are calling the wrong function, or the function was written incorrectly), and so the best it can do is tell you how it's interpreting your (wrong) code, which isn't super helpful becuase it's not the code you meant to write anyway.
Often for me, if I'm puzzled then the best course of action is to change the code and see if I get a more understanable error. This one is particularly hard to interpret because it's a syntax problem and it's not busted syntax but rather the syntax means something other than what you thought, and it's going to be hard to figure that out from the error message. I guess the question to ask yourself in this case is why it thinks you are trying to use 5 as a function, and if you don't know about operator sections then you won't be able to answer this question for yourself.
6
u/Pleasant-Couple6236 1d ago
Because you're applying it to
(6 :: Integer). The parser parsesexpr expras a function application before the type checker is invoked, and only when it realises the "function" doesn't actually have a function type does it generate the error.That's not actually a problem. Haskell has operator sectioning which makes something of the form
(+ f x)perfectly valid syntax as long as the types line up, which is why you're not getting a parse error and your example actually proceeds to type checking.