Search code examples
haskellinstancetypeclass

Haskell: instances parameterized type one single type


I'm writing some parser code, which involves a parameterized type:

data Power i    = Power1 { charges :: i } | Power2 { charges :: i }

Where I know the type i will always be an Int (but is parameterized because I need it for another type class).
Now I want to make my Power type derive from Show (in a specific formatting, so I need to override it myself). As I know that i will only ever be Int, I tried:

instance Show (Power Int) where
    show (Power1 0)    = ""
    show (Power1 i)    = "some formatting involving i\n"
    show (Power2 0)    = ""
    show (Power2 i)    = "some other formatting involving i\n"

However, this won't compile, with the message

    • Illegal instance declaration for ‘Show (Power Int)’
        (All instance types must be of the form (T a1 ... an)
         where a1 ... an are *distinct type variables*,
         and each type variable appears at most once in the instance head.
         Use FlexibleInstances if you want to disable this.)
    • In the instance declaration for ‘Show (Power Int)’
   |
22 | instance Show (Power Int) where

Why isn't this possible?

Why is this so different to the compiler than?

instance (Show i) => Show (Power i) where

Solution

  • GHC says All instance types must be of the form (T a1 ... an) where a1 ... an are *distinct type variables*. That is defined in Haskell 98. To take data type with a concrete type as class parameter require FlexibleInstances extension. Because there is overwrapping instances problem.

    For example:

    newtype Foo a = Foo a
    
    class Bar a where
        baz :: a -> a
    
    instance Bar (Foo Int) where
        baz = ...
    
    instance Show a => Bar (Foo a) where
        baz = ...
    

    When you call baz 1, compiler confuses whether version should call. 1 is Int but also belongs Show class! Details written in this post, you should read it when you have time.