Search code examples
haskelloverlapping-instances

Moving function call into where clause breaks type checker with OverlappingInstances


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.


Solution

  • 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.