I was using OverlappingInstances
to make a pretty print class that would default to Show
whenever I didn't provide a custom instance for some type.
For some reason this seems to break whenever you use a where
clause or a let
expression.
{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}
class View a where
view :: a -> String
instance {-# OVERLAPS #-} Show a => View a where
view = show
-- Works just fine
instance (View a, View b) => View (a, b) where
view (a, b) = "(" ++ view a ++ ", " ++ view b ++ ")"
-- Does not work
instance (View a, View b) => View (a, b) where
view (a, b) = "(" ++ a' ++ ", " ++ b' ++ ")"
where
a' = view a
b' = view b
-- Does not work
instance (View a, View b) => View (a, b) where
view (a, b) = let
a' = view a
b' = view b
in "(" ++ a' ++ ", " ++ b' ++ ")"
Now if I remove the default overlapping instance all of the other instances work just fine.
I am hoping someone could explain to me why this happens, or is it just a bug?
The specific errors I get for each of them are:
Could not deduce (Show a) arising from a use of ‘view’ from the context (View a, View b) bound by the instance declaration at ...
Could not deduce (Show b) arising from a use of ‘view’ from the context (View a, View b) bound by the instance declaration at ...
So for some reason where
/ let
are tricking the type checker into thinking View
requires Show
, when it doesn't.
Thanks @dfeuer for providing an alternative approach, but I figured I should write the fairly quick fix for the question itself:
{-# LANGUAGE MonoLocalBinds #-}
Once that is put at the top of the file everything works just fine. From the research I did it seems like certain extensions (I guess including OverlappingInstances
) poke holes in local binding type checking, and MonoLocalBinds
sacrifices using local binds polymorphically in order to fix those holes.