overlapping instances in 7.10.1

Sergei Meshveliani mechvel at botik.ru
Mon Jun 15 20:03:27 UTC 2015

On Mon, 2015-06-15 at 09:29 +0000, Simon Peyton Jones wrote:
> |  This is why I think that  ghc-7.8.3  treats the OI notion in a more
> |  natural way than ghc-7.10.1 does.
> |  May be, ghc-7.10.1 has a better technical tool for this, but  ghc-
> |  7.8.3 corresponds to a natural notion of OI.
> |  
> |  Can GHC return to a natural OI notion?
> |  Or am I missing something?
> Well it all depends what you mean by "natural". To me it is profoundly 
> un-natural to deliberately have the same type-class constraint solved in 
> two different ways in the same program!
> To require this would prevent cross-module specialisation. If I have	
> 	f :: C a => a -> a
> in one module, and I specialise it to
> 	f_spec :: [Int] -> [Int]
> in one module, I want to be free to re-use that specialisation in other modules. 
>  But under your "natural" story, I cannot do that, because (C [Int]) might be 
> resolved differently there.

Now, I give a simple (and a very contrived) example illustrating of how
overlapping instances  (OI)
are used in DoCon.
Also this example is made after the sample that Simon has given in his
recent letter:

> GHC generally assumes that if it generates   
(the instance) 
> (C T) in one place, then it can use that anywhere in the program that
> (C T) is needed.  That is, there is only one (C T) dictionary.
> But suppose you have overlapping instance in different modules; say
> module A where instance C [a]
> module B where import A; instance C [Maybe a]
> If you use (C [Maybe Int]) in A, then of course we won’t see the 
> instance in B.  So you’ll get a different dictionary than if you 
> compute C [Maybe Int] in module B.
> In short, overlapping instances are OK, but it’s best to put them in 
> the same module as the instances they overlap. 

My example:

module A where
class Att a where att :: a -> (Int , Maybe Int)

instance {-# OVERLAPPING #-}
         Att [a] where  att xs = (length (reverse xs) , Nothing)

f :: [Maybe Int] -> (Int , Maybe Int)
f = att

module Main where
import A

instance {-# OVERLAPPING #-}
         Att [Maybe a] where  att mbs = (length mbs , Just 1)

mbs = []  :: [Maybe Int]

main = putStr (shows (f mbs) "\n")          -- (I)   (0 , Nothing)
       -- putStr (shows (att mbs) "\n")     -- (II)  (0,  Just 1)

Att  stands for the class C of Simon's letter.
It means  "certain attributes of a value, and also of its type".
The value  Nothing  for the second part of (att a) means that the second
component of attributes is not definitely known for this particular
length (reverse xs)  imitates a non-optimal method for computing  att
in the instance for  a => [a].  This instance is very general, so that
the first component of attributes is evaluated in-efficiently (but
correct), the second component has a correct value, but highly
indefinite (few information derived).

The function  f  uses the instance  Att [Maybe Int],  
and it is satisfied with the generic instance given in  A.hs.
Because a) at this stage of the project there is not enough
functionality to implement a better special method  b) for this
particular case in this module the generic instance is sufficient. 

Main.hs  defines a special instance of Att for  [Maybe a].
This instance in more special than the one defined in  A.hs.
And the value  (att mbs)  has the first component evaluated more
efficiently than for the generic  (Att [a])  instance.  But the value is
the same.
The second component even has a different value in the result. But it is
considered as correct
(this is similar to the situation of: 
"generally the speed is  2 < s < 8,  
and in this the special case it is  3").

The call  (att mbs)   in `main' uses a different dictionary for  Att
than the call  A.f (mbs).

Both  ghc-7.8.3  
            (with -XFlexibleInstnce -XOverlappingInstances  in the call)
and   ghc-7.10.1  
            (with -XFlexibleInstnce  in the call)

give the same results in 'main':
(0, Nothing)  for the line (I)   
(0, Just 1)   for the line (II).

And I thought that everything is set naturally in this program.

Simon, you would state that
  as  A.f and  (Main.att mbs)  use the instances of  Att  for the 
  same type  [Maybe Int],
  "overlapping instances are OK, but it’s best to put them in the same 
   module as the instances they overlap". 

I do not understand the grammar of the phrase in quotes, either it has a
typo or this is too difficult English for me.
Anyway: does this mean for this particular example, that it is highly
desirable to set the two above instance declarations in the same module

1) What if they are in different modules, like in the above example.
   What unnatural may happen -- for example?

2) At least  ghc-7.8.3  and  ghc-7.10.1  do the same in this example. 
   May be, you can change this example a bit to make  ghc-7.8.3 and  
   ghc-7.10.1 diverse, so that my example bug becomes visible?
   (they diverse on  7.10.1-errReport-may23-2015.zip  but this bunch of 
    modules is too complex).
3) It is not practical to join the above  A.hs  and  Main.hs  into one
module. Because generally, between A.hs and Main.hs there are added many
modules with much functionality, and the instance implementation in Main
uses this functionality. This will be similar as putting all the
developed library into one module.

Please, advise,


More information about the Glasgow-haskell-users mailing list