Slides 28-31 of this presentation:
…may also be of interest.
Slides 28-31 of this presentation:
…may also be of interest.
Two classics that I think are worth touching on:
Foldable
constraint before they’ve had a chance to grapple with the monomorphic list functions.$ 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.
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!
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.
This sounds really useful and helpful!
[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.
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 Just
s 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
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>
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.
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.
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.
Very strange. I reported it here: Doesn't seem to be indexed well by search engines · Issue #537 · haskellfoundation/error-message-index · GitHub
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?
That sounds similar to the Haskell Error Index. That’s a great resource.
I’m writing something a bit different: some concrete suggestions for how to deal with type errors that point to the wrong place or are hard to understand/fix for some other reason. These are often totally “normal” type errors that are confusing because of how type inference works or because code is a bit too polymorphic or weird.
Errors related to rank-n-types often trip people up, like the one in this Reddit thread.
A tiny example:
foo :: forall x . Show x => Maybe x -> [(String,x)]
foo m = maybeToList $ (\z -> (show z,z)) <$> m
bar :: (forall x . Maybe x -> [(String,x)]) -> Maybe x -> [(String,x)]
bar f m = f m
baz :: [(String,Int)]
baz = bar foo (Just 5)
which results in:
• No instance for ‘Show x’ arising from a use of ‘foo’
Possible fix:
add (Show x) to the context of
a type expected by the context:
forall x. Maybe x -> [(String, x)]
• In the first argument of ‘bar’, namely ‘foo’
In the expression: bar foo (Just 5)
In an equation for ‘baz’: baz = bar foo (Just 5)
One in the wild:
<interactive>:2:1: error:
• Couldn't match representation of type ‘a0’
with that of ‘haskell-gi-base-0.26.8:Data.GI.Base.BasicTypes.ManagedPtr
()’
arising from a superclass required to satisfy ‘Coercible
a0
(haskell-gi-base-0.26.8:Data.GI.Base.BasicTypes.ManagedPtr
())’,
arising from a superclass required to satisfy ‘haskell-gi-base-0.26.8:Data.GI.Base.BasicTypes.ManagedPtrNewtype
a0’,
arising from a superclass required to satisfy ‘haskell-gi-base-0.26.8:Data.GI.Base.BasicTypes.GObject
a0’,
arising from a use of ‘passwordLookupSync’
• In the expression:
passwordLookupSync
Nothing (Map.fromList [("key", "value")]) Nothing
In an equation for ‘it’:
it
= passwordLookupSync
Nothing (Map.fromList [("key", "value")]) Nothing
It is still being diagnosed, I would have no idea where to start with this.