Examples of Haskell Type Errors

How beginners are the beginners you are writing the article for?

Do you consider “Ambiguous type variable” valid errors? In my experience they bite me a lot when I am using a parser combinator library.


Something straight out of the repl:

λ> foldr (+) 0 1

<interactive>:9:1: error: [GHC-39999]
    • Could not deduce ‘Foldable t0’
      from the context: (Foldable t, Num b, Num (t b))
        bound by the inferred type for ‘it’:
                   forall {t :: * -> *} {b}. (Foldable t, Num b, Num (t b)) => b
        at <interactive>:9:1-13
      The type variable ‘t0’ is ambiguous
      Potentially matching instances:
        instance Foldable (Either a) -- Defined in ‘Data.Foldable’
        instance Foldable Maybe -- Defined in ‘Data.Foldable’
        ...plus three others
        ...plus 26 instances involving out-of-scope types
        (use -fprint-potential-instances to see them all)
    • In the ambiguity check for the inferred type for ‘it’
      To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
      When checking the inferred type
        it :: forall {t :: * -> *} {b}. (Foldable t, Num b, Num (t b)) => b

which is a lot of output for “you forgot square brackets”.

5 Likes

Another one by me:

1 Like

Yeah, that’s a good level, and I might use that as an example for how to parse visually confusing error messages. There’s a lot of noise there!

Slides 28-31 of this presentation:

…may also be of interest.

3 Likes

Two classics that I think are worth touching on:

  • Beginners will encounter all sorts of errors involving a Foldable constraint before they’ve had a chance to grapple with the monomorphic list functions.
  • It’s very easy to use numeric literals in ways that generate confusing error messages:
$ ghci 
GHCi, version 9.6.5: https://www.haskell.org/ghc/  :? for help
ghci> 1 + True

<interactive>:1:1: error: [GHC-39999]
    • No instance for ‘Num Bool’ arising from the literal ‘1’
    • In the first argument of ‘(+)’, namely ‘1’
      In the expression: 1 + True
      In an equation for ‘it’: it = 1 + True

That is not a helpful way to say “numbers are not booleans”. I know why it is so, but a beginner does not. It used to be worse on older GHCs, where an errant numeric literal could conjure up all sorts of errors about FlexibleContexts being disabled.

4 Likes

We wrote a paper a while ago on localizing the source of type errors (https://dl.acm.org/doi/10.1145/3622812) and also conducted a user study. Most of the type error literature uses OCaml examples of faulty programs, but they can be easily adapted to Haskell since they only use the common Hindley-Milner subset. For example, the authors of https://dl.acm.org/doi/10.1145/3138818 shared their dataset of examples that we also used in our paper. But one of the best starting points is the great survey article https://dl.acm.org/doi/10.1145/3344429.3372508 which contains almost 220 references!

3 Likes

I wonder if a package that defined lots of bogus instances with Unsatisfiable preconditions could be helpful for beginners.

Something like

instead of

Instances for IO and functions could be particularly helpful I think.

10 Likes

This sounds really useful and helpful!

2 Likes

[EDIT: Oh, I said what @danidiaz had already said! Examples of Haskell Type Errors - #10 by danidiaz]

Although, I wouldn’t say it should be “a package”, I would say it should be base.

Unsatisfiable is a good start, but if we had apartness constraints we could do some of the stuff in this 2017 talk from Compose Melbourne about bringing domain-specific error messages into libraries. The “you haven’t got your monad licence” example is probably the most beginner-relevant.

3 Likes

I recently had one of these in our production code.

I unfortunately don’t remember how to make it happen again, but I believe it was cause by adding an argument to the type signature of a function that uses do notation, but forgetting to add the argument on the value level (before the =)
(or I did both, but then when reverting my changes forgot to remove the argument from the type signature, which results in the same situation)

Because the do notation was our own monad, and some return values were List/Maybe/etc. and I think the value I added also had Maybe, the do notation was now implicitly confusiing which Monad was used where and I kept juggling with Justs and whatnot, but adding one just made the error weirder (adding expected (Maybe a), but found Maybe (Maybe a) etc. where it was expected a, but found (Maybe a) before I added the Just)

Sorry for being vague, but maybe someone knows how to elicit this situation :man_shrugging:

1 Like

I unfortunately don’t remember how to make it happen again, but I believe it was cause by adding an argument to the type signature of a function that uses do notation, but forgetting to add the argument on the value level (before the =)

Perhaps that was due to (->) r being monadic?

# ghci
GHCi, version 9.8.2: https://www.haskell.org/ghc/  :? for help
ghci> :i (->)
type (->) :: * -> * -> *
type (->) = FUN Many :: * -> * -> *
  	-- Defined in ‘GHC.Types’
infixr -1 ->
instance Monoid b => Monoid (a -> b) -- Defined in ‘GHC.Base’
instance Semigroup b => Semigroup (a -> b) -- Defined in ‘GHC.Base’
instance Applicative ((->) r) -- Defined in ‘GHC.Base’
instance Functor ((->) r) -- Defined in ‘GHC.Base’
instance Monad ((->) r) -- Defined in ‘GHC.Base’
ghci>
1 Like

You’d think, but any mention of function weirdness would trigger me to think “oh did I screw up the arguments”.
It was exactly because it didn’t mention any of that that it took me a while to figure it out.

Now it might be that one of the monadic things happening in the function was inferred as being the function monad, but with all the potential monadic happenings, it sort of dislocated which part was which monad.
I think I finally figured it out by adding type annotations one by one to check which parts were which monad, and if they were what I expected them to be.

2 Likes

Seems no one has mentioned the Haskell error index yet. Probably because type errors do not feature there much. However, the index aims to provide examples like this one.
Perhaps the contents of your blog post eventually can help to update the error index examples.

A particularly hard-to-spot error (at least for me) were the effects of simplified subsumption, which was introduced in GHC 9 and can lead to very strange and cryptic error messages when higher-rank types (e.g. lenses) are involved.

But there are also very common issues that can easily bite beginners, e.g. missing ScopedTypeVariables. The nasty bit is that adding a type signature makes things worse while we keep telling beginners that type signatures are helpful.

1 Like

Lovely example, I wouldn’t have thought of that edge case myself.

Looking forward to this blog post!

It’s a great initiative, but sadly error codes are ungooglable. For “GHC-39999” (with and without quotes) from this thread, it doesn’t lead me to Haskell Error Index, actually, at all (!!). Yes, I did check every google result, I did learn that GHC is a currency sign for Ghanaian cedi, but no error index in sight. It’s even worse, “Haskell Error Index” (with and without quotes) doesn’t lead me to the page also (this time I did check first 3 result pages).

Something clearly doesn’t work with that website.

6 Likes

Very strange. I reported it here: Doesn't seem to be indexed well by search engines · Issue #537 · haskellfoundation/error-message-index · GitHub

3 Likes

SML/NJ Error and Warning Messages lists (normal) error messages returned by the SML/NJ compiler. From your post, it seems that you want to create something similar but for GHC/Haskell?