
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 instance. 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). Right? 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) and (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, ------ Sergei