quickcheck icon indicating copy to clipboard operation
quickcheck copied to clipboard

Remove Overlapping pragmas

Open treeowl opened this issue 9 years ago • 6 comments

Use a closed type family to avoid the need for overlapping instances. This shouldn't really affect anything, but it makes instance selection more explict.

treeowl avatar Feb 22 '16 03:02 treeowl

Hmm... This actually seems to prevent some things from working. Some things that maybe shouldn't work? I need to consider this a bit more.

treeowl avatar Feb 22 '16 04:02 treeowl

Specifically, GHC seems happy to fall back on the overlapping "not this one" instance quite readily when confronted with the question of whether, e.g., a ~ (a,b). However, it's more conservative about type family reduction out of fear of type families that loop and thus can produce infinite types like (a,(a,(a,..... But this affects the type accepted for, e.g., \a b -> genericShrink (a, b). It's not clear to me whether this can actually cause problems for realistic uses of genericShrink.

treeowl avatar Feb 22 '16 05:02 treeowl

Hi,

It is quite worrying if genericShrink can miss shrink candidates because of GHC picking the wrong instance. Do you have an example (even if it's artificial) where this can happen? I'd like to understand this better.

Note that a ~ (a, b) is indeed impossible because the only solutions are infinite types. So GHC is right to pick the other instance in this case.

I can't merge this change directly because closed type families are only supported from GHC 7.8 upwards. But if there are cases where GHC picks the wrong instance with OverlappingInstances, then we could use the preprocessor to select either this implementation or the existing one depending on the GHC version.

nick8325 avatar Feb 23 '16 13:02 nick8325

I don't actually know if this can go wrong. Type family reduction is very conservative because GHC can't always recognize infinite types. You can write

type family Ugh a where
   Ugh a = (a, Ugh a)

This can, of course, throw the type checker into an infinite loop when it's reduced, which is okay. But the existence of such families precludes GHC from implementing a general a /~ (a,b) rule, so when you apply

type family (==) a b where
  a == a = 'True
  a == b = 'False

as a == (a,b) for unknown a, it refuses to reduce and considers itself stuck. This is (at least for now) necessary to maintain type safety. The overlapping instance mechanism is more liberal. It seems satisfied to conclude that they're not equal and select the more general instance. This, at worst, risks incoherence, but not type safety failure.

treeowl avatar Feb 23 '16 13:02 treeowl

I prefer closed type families to overlapping instances as a matter of taste (though I don't really love either). It would, I think, take some experimentation to determine whether the type family implementation reduces under circumstances typical for Arbitrary instances.

treeowl avatar Feb 23 '16 13:02 treeowl

Yikes, I see. That's quite nasty!

I also prefer closed type families to overlapping instances. But I don't want to break QuickCheck on GHC 7.6 (still the default version on e.g. Debian stable) so let's make this change in the future.

nick8325 avatar Feb 23 '16 13:02 nick8325

Closing this as completed via #370

MaximilianAlgehed avatar Mar 21 '24 08:03 MaximilianAlgehed