Thanks @Tikhon. I found your terminology … em not altogether the terminology I would use:
- I think of a ‘bug’ as being where the program compiles (possibly with some warnings) and runs; but
- doesn’t produce the out put expected, or what the spec says. (Or what the spec would say if the client had thought of this scenario in advance …)
- ‘Bug’ includes things like running forever or stack overflow or numeric overflow.
- That Nine Rules book you mention says “Debugging usually means figuring out why a design doesn’t work as planned.” – which at least means it “works” somewhat. A program that doesn’t compile is nowhere near any kind of “working”.
What you’re describing I’d call ‘type mis-matches’/‘compile fails’ – error messages from the compiler because type unification fails. (Your write-up explicitly mentions Hindley-Milner type systems, but not unification – see the link from here on H-M.)
‘Haskell Types as Constraints’ – beware. ‘Constraint’ has a very specific meaning within Haskell’s type (class) system. Most of your examples in that section do not involve typeclasses.
Your ‘Type Signatures as Assertions’ example for me rather adds to my confusion. (Also beware ‘Assertion’ is another technical term in some programming languages, usually about values not types):
An important aspect of Haskell’s type checking and type inference is that type signatures act like assertions. That is, when Haskell sees x :: Int
, it will take this as given for the rest of the code. This is true even if x
is defined to be something that can’t be an Int
.
If we load the following code, we’ll get two type errors: …
Ok you’ve set me up to expect to see x :: Int
. So at first reading I’m perplexed why you didn’t and why those two messages don’t talk about the x = False
line. You do go on to do that afterwards, but by then I’m bamboozled by the No instance for (Num Bool) arising from a use of ‘+’
message. (Or at least I would be if I were a beginner who doesn’t already know what you’re doing there.) I suggest you cover first what happens with the signature; then go on to the more bamboozling messages due to omitting the signature. That’s reinforcing the best practice of giving signatures. (And that’s one diagnostic technique: give signatures for every variable; also put inline signatures ::
for every call to a class method.)
BTW 1:
(Side note: that second error is a great example of arbitrary attribution: why does it point to +
and not *
as the reason we need a Num
instance? …
Yeah, good case in point of error reports hand-waving to the general vicinity of the mis-match rather than anywhere precise. Try this for your last line if you want a totally misleading message:
z = 2 > x + y
BTW 2: Is deferring type errors a Good Thing to be suggesting to your audience?
- This type error is actually a warning because I have the
-Wdeferred-type-errors
flag turned on. This flag is great for development because it lets the compiler surface more type errors and lets you experiment with the working parts of your code even if other parts don’t typecheck.︎
Doesn’t deferring tend to increase the number of errors reported? (In a knock-on effect from one error.) And make it more difficult to narrow down where the initial cause is?