error-messages
error-messages copied to clipboard
Better error messages for Num literals
I hope, this is the correct way to post this, since this repo seems to be pretty new.
In my experience, one of the most annoying error messages in GHC is when you try to assign a number literal to a type which does not implement Num
.
x :: String
x = 5
What you would expect as a beginner is something like Could not match type Int with String, but because Num literals are overloaded in Haskell, you actually get the following.
<interactive>:2:18: error:
• No instance for (Num String) arising from the literal ‘5’
• In the expression: 5
In an equation for ‘x’: x = 5
If the source of this No instance for (Num String) part is a literal, the compiler could provide a more useful message. Something like Expected a String, but got a Num literal seems a lot more helpful to newbies.
Excellent suggestion -- thank you! (It's excellent because it includes a concrete test case, the existing error message, a concrete new message, and even a way for it to be implemented.)
This looks like a clear improvement to me, and should be easily actionable. But before I run off and make a GHC ticket for this, are there other suggestions out there? It would be great for a few people to come by and, say, thumbs-up the report to know that others agree with this change.
Also in the case of non-literals the wording of the current error message suggest that an instance should be defined, but I think that there are many cases, probably even the majority, where defining and instance is not the right thing to do. Maybe a more neutral message that is more like a plain type mismatch would be better:
Couldn't match expected type ‘String‘ with a type that has a ‘Num‘ instance
And there could be a suggestion below it:
Perhaps you want to define an instance of ‘Num‘ for the type ‘String‘
I still don't think it is ideal, but it is more neutral. Maybe someone can improve it even further.
A rephrasing may be helpful, but I care about not losing the crucial piece of information that it would be fine, were there an instance of Num
for String
.
@ByteEater-pl how do your feel about the suggestion I added to my comment:
Perhaps you want to define an instance of ‘Num‘ for the type ‘String‘
That can also be added to the improved error message for literals that @Innf107 suggests.
@noughtmare, yes, it'd do the job. However, for the first sentence I find @Innf107's suggestion clearer. How about something like this then:
Cannot unify types. Expected a value of (inferred|declared|partially declared) type String, but (got|found) a value of (inferred|declared|partially declared) type a which is constrained by: • Num a Perhaps you want to add an instance of Num for the type String.
and of course the source location.
I hope, this is the correct way to post this, since this repo seems to be pretty new.
Thank you for taking the time to open the discussion on this one.
In my experience, one of the most annoying error messages in GHC is when you try to assign a number literal to a type which does not implement Num.
This is a great message to start with!
Something like Expected a String, but got a Num literal seems a lot more helpful to newbies.
IMHO, I believe would help to add this "Expected a Foo, but got a Bar" to the existing message (probably as a prefix), keeping the rest of the message as it is.
Perhaps you want to define an instance of ‘Num‘ for the type ‘String‘
It might be nice to append this to the existing message as a suffix, it's useful regardless of the "expected foo.." message as a prefix.
Does someone feel able to bring this to an actionable conclusion, ideally creating a GHC ticket with a specific request? Thanks!
No further progress? 😥
IMHO, I believe would help to add this "Expected a Foo, but got a Bar" to the existing message (probably as a prefix), keeping the rest of the message as it is.
This sounds good to me. I think this may the easiest way to clarify the error message without losing any info
E.g:
<interactive>:2:18: error:
• Expected a value of type `String`, but got a `Num literal`
• No instance for (Num String) arising from the literal ‘5’
• In the expression: 5
In an equation for ‘x’: x = 5
Hint: Perhaps you want to define an instance of ‘Num‘ for the type ‘String‘
How does that sound?
I'd like to suggest a few tweaks:
<interactive>:2:18: error:
• Expected a value of type `String`, but got a number literal
• No instance for (Num String) arising from the literal ‘5’
• This would be accepted if there were an `instance Num String',
but you probably do not want to make this orphan instance for a type
you did not define
• In the expression: 5
In an equation for ‘x’: x = 5
where the "but you probably" bit is added whenever the missing instance includes only types defined outside the current package. That is, I really don't want newcomers responding to this error by defining instance Num String
-- although I do want them to know that doing so would actually fix the error (as that furthers their knowledge of the language).
Also, note that I changed Num literal
to number literal
and dropped the quotes.
If the literal is, say, 1.2
, do we want to say "decimal literal" or "fractional literal" or "floating-point literal" (boo)? I'm fine with calling both 3
and 3.14
"number literal"s, but maybe others feel differently.
where the "but you probably" bit is added whenever the missing instance includes only types defined outside the current package.
Oh yea that's a cool idea - that extra context would be really useful for beginners I imagine.
If the literal is, say, 1.2, do we want to say "decimal literal" or "fractional literal" or "floating-point literal" (boo)? I'm fine with calling both 3 and 3.14 "number literal"s, but maybe others feel differently.
I also think "number literal" is probably sufficient. All in all this new error message feels pretty solid. We can probably draft the ticket for GHC now if everyone else feels good with it
I think it's ready for the GHC ticket. Would you mind doing the honors there? :) Thanks!
Ticket created here: https://gitlab.haskell.org/ghc/ghc/-/issues/21086
Happy to work on this. 😄
Per https://github.com/haskell/error-messages/issues/44 I think we can do this without baking a bunch of logic in GHC, and keeping it in libraries instead.
instance TypeError (Text "strings are not numbers") => Num String
Actually gets us pretty close to what we want. Can we do a little GHC to make such a pure-library solution 100% adequate? This will slow us down in the short term, but I think allow us to make more things better faster in the long term, because all libraries are free to similarly point us "instanes that will never be written" for the user's benefit, rather than base
getting special treatment.
I actually think base
should get special treatment, because of its pervasiveness. We should also allow libraries to define negative instances, if they like, but if we can do better for very common errors that newcomers encounter, we should not let "but it's not general enough" stop us from doing so (in my opinion).
I wrote in #44 that it's not just a aesthetic distaste for ad-hoc solution that motivates, but also a fear of creeping additional wired in identifier and GHC--library couplings. I am not confident we'll be able to "stop" as base
, e.g. if people want to use Text
rather than String
they will not want error message to get worse. I generally want stuff to get less entangled not more, and I want to fine ways error messages can dovetail with, not fight, that general goal.
To be clear, this would not require wiring-in. It would require something called a known-key name, which means that GHC would have to be aware of the package name and the defining module. That is a coupling, and I agree that coupling is bad. But I think the benefit -- a clearer error message for everyone to enjoy -- is worth this cost. As we gain experience with these sorts of specialized error messages, we might be able to generalize the approach in a way that does not require the coupling. But I don't want the perfect to become the enemy of the good -- and, furthermore, I think that building out these specific error messages will help us understand the expressiveness we would need in the general mechanism.
That sounds good.