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
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
More information about the Glasgow-haskell-users
mailing list