returning to instance overlap

Bertram Felgenhauer bertram.felgenhauer at googlemail.com
Sat Jan 19 12:21:13 EST 2008


Serge D. Mechveliani wrote:
> Dear GHC developers,

I'm not one of them, but maybe I can help anyway.

> can you exaplain, please, how to use overlapping instances? 
> 
> For example, the following program uses overlapping instances for  
> DShow [a]  and  DShow String,  but ghc-6.8.2 reports an error:
> 
> -----------------------------------------------------------------
> class DShow a where  dShows :: a -> String -> String
> 
> instance DShow Char where  dShows = showChar 
> instance DShow Int  where  dShows = shows 
> 
> instance DShow a => DShow [a]                                   -- (1)
>   where 
>   dShows _ = showString "contrived show value"    
> 
> instance DShow String where  dShows = shows                     -- (2)
> 
> f :: DShow a => [a] -> String 
> f               xs  =  dShows xs "" 
>                        -- dShows (head xs) ""    -- compare to this
> 
> main = putStr (shows (f "abc", f [1, 2, 3 :: Int]) "\n")
> -----------------------------------------------------------------

Does the trick from the Show class help you? Simplified, it is
the following:

class Show a where
    show :: a -> String
    showList :: [a] -> String
    showList = default implementation for showList

instance Show Char where
    show = ...
    showList = specific implementation for String

instance Show a => Show [a] where
    show = showList

By putting the showList function into the Show class, we get
a special treatment of Strings in Haskell 98, without any overlapping
instances at all.

> I compile this under 
>   -fglasgow-exts -fallow-overlapping-instances 
>   -fallow-undecidable-instances  
>   -fno-warn-overlapping-patterns -fwarn-unused-binds 
>   -fwarn-unused-matches -fwarn-unused-imports
> 
> But the  ghc-6.8.2  compiler reports  
> 
>    Overlapping instances for DShow [a]
>       arising from a use of `dShows' at IOverlap.hs:21:23-34
>     Matching instances:
>       instance [overlap ok] (DShow a) => DShow [a]
>         -- Defined at IOverlap.hs:(14,0)-(16,45)
>       instance [overlap ok] DShow String
>         -- Defined at IOverlap.hs:18:0-42
>     (The choice depends on the instantiation of `a'
>      To pick the first instance above, use -fallow-incoherent-instances
>      when compiling the other instance declarations)
>     In the expression: dShows xs ""
>     In the definition of `f': f xs = dShows xs ""

If you haven't done so already, you should read
    http://www.haskell.org/ghc/docs/latest/html/users_guide/type-class-extensions.html#instance-overlap
    (http://tinyurl.com/2cq3zm)

What ghc is saying here is that there are two instances of DShow that
can potentially match DShow [a]. That's bad because the compiler might
pick a 'wrong' instance if it proceeds.

Even with -fallow-incoherent-instances, the compiler is free to choose
either instance, although it will try to use the most specific instance
that fits the requirements.

In your case, however, the most specific instance of DShow for [a]
that fits the requirements (which are that the instance can be
derived from the function's context alone) is DShow a => DShow [a],
so the compiler picks that one. (Btw, one key point to keep in mind
here is that ghc does not have any run time type information).

You can fix your program by declaring

    f :: DShow [a] => [a] -> String

or even

    f :: (DShow a, DShow [a]) => [a] -> String

which will postpone the selection of the instance of DShow [a] to
the point where f is called.

> 3. Also DoCon uses overlapping instances, and works under ghc-6.8.2, 
>    somehow (probably, I need to recall what precisely this usage is). 

There's only trouble with overlapping instances when you have several
instances for the same type with different behaviour. (I don't know
whether that's true or not for DoCon.)

> 4. Maybe, it is good compile the above declaration
> 
>   f :: DShow a => [a] -> String 
>   f xs = dShows xs "" 
> 
> by relating to  dShows  the _set_   S = {dShows of (1), dShows of (2)}   
> of two instances,
> putting to the code the set of two implementations.

That might work to some extent, but I think there are good reasons not
to do that.

1) the number of different implementations of f that you have to keep
   around can be very large. Consider

   f :: (DShow a, DShow b, DShow c) => [a] -> [b] -> [c] -> String
   f as bs cs = dShows as . dShows bs . dShows cs $ ""

   which will need 8 copies of f to work.

2) with higher order functions, the final type the function will get
   called with may never be known until runtime, say,

   g :: (forall a . DShow a => [a] -> String)
     -> Either String [Int] -> String
   g f (Left a)  = f a
   g f (Right b) = f b

   (consider g (\xs -> dShows xs "") -- which dShows should be used here?)

HTH,

Bertram


More information about the Glasgow-haskell-users mailing list