Opened 10 months ago

Closed 9 months ago

Last modified 6 months ago

#9242 closed feature request (fixed)

Implement {-# OVERLAPPABLE #-} and {-# INCOHERENT #-} pragmas

Reported by: simonpj Owned by: diatchki
Priority: normal Milestone:
Component: Compiler Version: 7.8.2
Keywords: Cc:
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: None/Unknown Test Case:
Blocked By: Blocking:
Related Tickets: Differential Revisions:

Description (last modified by simonpj)

The language extensions -XIncoherentInstances and -XOverlappingInstances (described in the user manual) apply to every instance declaration in the module. Internally, however, GHC records this information on a per-instance-declaration basis.

It would be much better for the programmer to be able to control these matters on a per-instance-declaration basis. Thus:

instance Show a => Show [a] where
  {-# OVERLAPPABLE #-}
  ...

instance Show [Char] where
  ...

We came across the need for this when discussing the Typeable instances. We have a generic instance like this:

instance (Typeable f, Typeable a) => Typeable (f a) where ....

but also want

instance KnownNat n => Typeable (n :: Nat) where ...

If we seek (Typeable (x::Nat)), we should pick the second instance even though in principle you might imagine that x might be instantiated to (f a), for some f and a. But we know that there are no type constructors f of kind blah -> Nat, so this can never happen and it's safe to pick the second instance. So a local {-# INCOHERENT #-} pragma for this particular instance declaration would be very useful to express this fact.

instance KnownNat n => Typeable (n :: Nat) where
  {-# INCOHERENT #-}
  ...

Quite apart from this special case, it's plain better to have per-instance control

  • Rather than a remote per-module flag, the pragma draws attention that this particular instance is incoherent, overlappable or whatever.
  • The current situation almost certainly means that more instances are marked overlapping than are really necessary.

I have some design questions.

  • Where should the pragma appear, syntactically?
       instance {-# OVERLAPPABLE #-} Show a => Show [a] where ...
    or 
       instance Show a => Show [a] {-# OVERLAPPABLE #-} where ...
    or
       instance Show a => Show [a] where
          {-# OVERLAPPABLE #-}
    
    Remember that SPECIALISE INSTANCE pragmas appear in the third of the above positions, so I mildly favour that.
  • The user manual (link above) allows overlap if either instance is compiled with -XOverlappingInstances (see the rules towards the end of 7.6.3.5). It would be more explicit to have two pragmas, one to say "I can be overlapped" and one to say "I can overlap something else". The rule would then be that "either IX is marked {-# OVERLAPPABLE #-} or IY is marked {-# OVERLAPPING #-}.

Change History (38)

comment:1 Changed 10 months ago by simonpj

  • Owner set to diatchki

Iavor has agreed to execute on this.

Get rid of Unify.validKindShape as a result.

Simon

Last edited 10 months ago by simonpj (previous) (diff)

comment:2 Changed 10 months ago by simonpj

  • Description modified (diff)

comment:3 Changed 10 months ago by simonpj

  • Description modified (diff)

comment:4 Changed 10 months ago by Iavor S. Diatchki <iavor.diatchki@…>

In 6290eeadf61a40f2eb08d0fd7ef1f3b7f9804178/ghc:

Overlapable pragmas for individual instances (#9242)

Programmers may provide a pragma immediately after the `instance` keyword
to control the overlap/incoherence behavior for individual instances.
For example:

    instance {-# OVERLAP #-} C a where ...

I chose this notation, rather than the other two outlined in the ticket
for these reasons:

   1. Having the pragma after the type looks odd, I think.
   2. Having the pragma after there `where` does not work for
       stand-alone derived instances

I have implemented 3 pragams:

   1. NO_OVERLAP
   2. OVERLAP
   3. INCOHERENT

These correspond directly to the internal modes currently supported by
GHC.  If a pragma is specified, it will be used no matter what flags are
turned on.   For example, putting `NO_OVERLAP` on an instance will mark
it as non-overlapping, even if `OVERLAPPIN_INSTANCES` is turned on for the
module.

comment:5 Changed 10 months ago by Iavor S. Diatchki <iavor.diatchki@…>

In b7f9b6a7c800da98d5ba17c45df2a589cc999975/ghc:

Eliminate `Unify.validKindShape` (#9242)

comment:6 Changed 10 months ago by simonpj

What about the last bullet point of the ticket description? The relationship between two overlapping instances is not symmetrical. I suggested "OVERLAPPABLE" and "OVERLAPPING". Yes, it's a bit more fiddly, but much more explicit. Oh, and I suppose you should be able to have both.

instance {-# OVERLAPPABE #-} C a where
instance {-# OVERLAPPABLE, OVERLAPPING #-} C [a] where ...
isntance {-# OVERLAPPING #-} C [Maybe a] where ...

Here the middle one overlaps C a and is overlapped by C [Maybe a].

Or is this over-elaborate?

Simon

comment:7 Changed 10 months ago by simonpj

PS: could you also update the user manual?

Are we going to deprecate -XOverlappingInstances in favour of these pragmas? (I vote yes.) In which case that's something else to do.

Thanks

Simon

comment:8 Changed 10 months ago by tibbe

There's been talk about implementing a -XNoOrphanInstances flag, which should make overlapping always safe (I think). If so perhaps it's useful to have a -XOverlappingInstances club to allow overlaps everywhere?

comment:9 Changed 10 months ago by simonpj

Safe or not, I still think it'd be better to signal it locally. Clubs are rather blunt instruments.

Simon

comment:10 Changed 10 months ago by goldfire

The interaction between a hypothetical -XNoOrphanInstances, Safety, and overlapping instances is subtle -- the flags have to be put potentially on different modules to get the Safety requirement to work out. For this reason and in order to avoid blunt instruments, I favor also deprecating OverlappingInstances.

For the bulk of you surprised by this mysterious -XNoOrphanInstances, it's an idea that Edward Kmett, Gershom Bazerman, and I cooked up a few months ago. More information in due course!

comment:11 Changed 10 months ago by diatchki

I like the OVERLAPPING and OVERLAPPABLE idea, and I was going to have a stab at implementing it. I just thought it'd be easier to start by simply exposing what GHC does first, before changing more things. So, this, and updating the manual is on my TODO list.

comment:12 Changed 9 months ago by diatchki

I thought some more about the OVERLAPPING and OVERLAPPABLE idea, but I couldn't come up with examples where I'd want to use that feature. So, for the time being, I'll leave the implementation as is, and document it accordingly.

Related to this: having looked at the code for the instance database, I think it would be quite easy to implement "instance chains", which eliminates the need for overlapping instances in pretty much all situations that I've encountered them. I outlined the details in ticket #9334.

comment:13 Changed 9 months ago by simonpj

As I say on the other ticket, I think it'll be a hard sell to replace overlapping instances with instance chains. So I'd still really like to get the per-instance {-# OVERLAPPING, OVERLAPPABLE #-} pragmas proposed above.

Would you still be willing to do that? Or, if not, would someone else?

Thanks

Simon

comment:14 Changed 9 months ago by diatchki

I know the code, so I don't mind doing it. Out of curiosity, do you have an example of when you may want to write just OVERLAPPING and not OVERLAPABLE or vice versa?

The way I understand it, they have the following meanings:

  • OVERLAPPING says "I am a most general instance" (i.e., can overlap, but can't be overlapped),
  • OVERLAPABLE says "I am a most specific instance" (i.e., can't overlap, can be overlapped).

So, they make some sense for the "edge-case" instances, but not for the ones inbetween, and I couldn't really think of good cases where I may want to put them on an instance.

comment:15 Changed 9 months ago by diatchki

As I started implementing this, it occurred to me that another interpretation of OVERLAPPING and OVERLAPPABLE is also possible:

  • OVERLAPPING:
    • may replace any more general instance;
    • may be replaced by more specific instances marked with OVERLAPPING.
  • OVERLAPABLE:
    • may be replaced by any more specific instance;
    • may replace more general instances marked with OVERLAPABLE.

This interpretation has the advantage of specifying how to interact with "normal" instances (i.e., ones compiled in modules without special overlapping flags). The drawback is that there is no way for an instance to be sure that it won't be replaced. Perhaps that's OK though---I think the current system does not provide a way to do this anyway.

See also #3877 for some related discussuion.

comment:16 Changed 9 months ago by Iavor S. Diatchki <iavor.diatchki@…>

In 97f499b56c5888740ddb147fb198c28a3c06bac7/ghc:

Implement OVERLAPPING and OVERLAPPABLE pragmas (see #9242)

This also removes the short-lived NO_OVERLAP pragama, and renames
OVERLAP to OVERLAPS.

An instance may be annotated with one of 4 pragams, to control its
interaction with other overlapping instances:

  * OVERLAPPABLE:
    this instance is ignored if a more specific candidate exists

  * OVERLAPPING:
    this instance is preferred over more general candidates

  * OVERLAPS:
    both OVERLAPPING and OVERLAPPABLE (i.e., the previous GHC behavior).
    When compiling with -XOverlappingInstances, all instance are OVERLAPS.

  * INCOHERENT:
    same as before (see manual for details).
    When compiling with -XIncoherentInstances, all instances are INCOHERENT.

comment:17 Changed 9 months ago by jstolarek

I think this should also be mentioned in the release notes for 7.10.

comment:18 Changed 9 months ago by simonpj

Great stuff, thank you. I'll now deprecate -XOverlappingInstances.

To finish this off I'd love someone to make INOCOHERET per-instance too!

Simon

comment:19 Changed 9 months ago by diatchki

We can already mark individual instances as INCOHERENT. For example, in Data.Typeable.Internal we have an instance like this:

instance {-# INCOHERENT #-} (Typeable f, Typeable a) => Typeable (f a) where ...

In the fourth bullet of the commit message (comment:16 above), I just meant that instances annotated with INCOHERENT work in the same way they did before.

comment:20 Changed 9 months ago by simonpj

Ah yes, thanks. As you say in comment:16, an instance can have just one of these four properties. But the rules in the user manual then don't seem quite right. They currently read thus (in my HEAD, somewhat improved from HEAD):

The willingness to be overlapped or incoherent is a property of the instance declaration itself, controlled as follows:

 * An instance is overlappable if it has an OVERLAPPABLE or OVERLAPS
   pragama, or if the module in which the instance appears is compiled
   with -XOverlappingInstances.

 * An instance is overlapping if it has an OVERLAPPING or OVERLAPS
   pragama, or if the module in which the instance appears is compiled
   with -XOverlappingInstances.

 * An instance is incoherent if it has an INCOHERENT pragama, or if
   the module in which the instance appears is compiled with
   -XIncoherentInstances.

Now suppose that, in some client module, we are searching for an instance of the target constraint (C ty1 .. tyn). The search works like this.

 * Find all instances I that match the target constraint; that is, the
   target constraint is a substitution instance of I. These instance
   declarations are the candidates.

 * Find all non-candidate instances that unify with the target
   constraint. Such non-candidates instances might match when the
   target constraint is further instantiated. If all of them are
   incoherent, proceed; if not, the search fails.

 * Eliminate any candidate IX for which both of the following hold:
   * There is another candidate IY that is strictly more specific;
     that is, IY is a substitution instance of IX but not vice versa.

   * Either IX is overlappable or IY is overlapping.

 * If only one candidate remains, pick it. Otherwise if all remaining
   candidates are incoherent, pick an arbitrary candidate.

As things stand, and in the implementation, INCOHERENT does not imply "overlappable" or "overlapping". And it probably should. After all, if you have two matching candidate instances

   (no pragma)    C [a]
   (incoherent)   C [Int]

you probably want to pick the latter not the former, whereas the current rules pick the former! Moreover, if there are two candidate instances

   (incoherent)  C [a]
   (incoherent)  C [Int]

again we want to pick the latter, whereas the current rules say "pick either".

The alternative to making INCOHERENT imply OVERLAPS is to treat overlapping and incoherent as separate things, so you can say

  instance {-# INCOHERENT, OVERLAPPABLE #-} C [a]

I don't advocate this -- it seems too elaborate.

So I suggest we change the spec above, and the impl to say

 * An instance is overlappable if it has an OVERLAPPABLE or OVERLAPS or INCOHERENT
   pragama, or if the module in which the instance appears is compiled
   with -XOverlappingInstances or -XIncoherentInstances.

 ...and similarly "overlapping"

Do you agree?

Last edited 9 months ago by simonpj (previous) (diff)

comment:21 Changed 9 months ago by simonpj

One more thing. Look at this note in InstEnv:

Note [Incoherent instances]
~~~~~~~~~~~~~~~~~~~~~~~~~~~
For some classes, the choice of a particular instance does not matter, any one
is good. E.g. consider

        class D a b where { opD :: a -> b -> String }
        instance D Int b where ...
        instance D a Int where ...

        g (x::Int) = opD x x  -- Wanted: D Int Int

For such classes this should work (without having to add an "instance D Int
Int", and using -XOverlappingInstances, which would then work). This is what
-XIncoherentInstances is for: Telling GHC "I don't care which instance you use;
if you can use one, use it."

Should this logic only work when *all* candidates have the incoherent flag, or
even when all but one have it? The right choice is the latter, which can be
justified by comparing the behaviour with how -XIncoherentInstances worked when
it was only about the unify-check (note [Overlapping instances]):

Example:
        class C a b c where foo :: (a,b,c)
        instance C [a] b Int
        instance [incoherent] [Int] b c
        instance [incoherent] C a Int c
Thanks to the incoherent flags,
        [Wanted]  C [a] b Int
works: Only instance one matches, the others just unify, but are marked
incoherent.

So I can write
        (foo :: ([a],b,Int)) :: ([Int], Int, Int).
but if that works then I really want to be able to write
        foo :: ([Int], Int, Int)
as well. Now all three instances from above match. None is more specific than
another, so none is ruled out by the normal overlapping rules. One of them is
not incoherent, but we still want this to compile. Hence the
"all-but-one-logic".

That makes sense, but it doesn't implement the rule in the manual (comment:20).
I think the last bullet should therefore read

 * If exactly one non-incoherent candidate remains, pick it.  If all
   remaining candidates are incoherent, pick an arbitary
   one. Otherwise fail.

Do you agree?

comment:22 Changed 9 months ago by simonpj

Sigh. One last observation. Read the Description of this ticket. I thought we'd end up with

instance {-# INCOHERENT #-} Typeable (n::Nat) where ...
instance (Typeable f, Typeable a) => Typeable (f a) where ...

The weird one is the Typeable (n:Nat) instance. Our reasoning is that we don't want to worry about instantiations of n. And this is what we wrote in the Description.

But actually the rules above mean that we have to write this

instance Typeable (n::Nat) where ...
instance {-# INCOHERENT #-} (Typeable f, Typeable a) => Typeable (f a) where ...

(and that is indeed what is in Data.Typeable.Internals today. The incoherent flag is on the innocent Typeable (f a) declaration.

Obviously incoherence is an area where we don't expect "right" answers. But the above does suggest that it could make sense to change the behaviour of incoherence, to this: ignore a non-candidate unifying instance when (and only when) there is an incoherent matching instance.

Here is another example to illustrate

class C a where
  op :: a -> Bool

instance {-# INCOHERENT? #-} C [a] where
  op x = True

instance {-# INCOHERENT? #-} C [Int] where
  op x = False

f :: [a] -> Bool
f x = op x

Without incoherence, f is rejected even with overlapping instances, because the choice depends on the instantiation of a. So, which instance needs {-# INCOHERENT #-} to make it acceptable? Today the answer is the second one; with the change proposed here, it'd be the first one.

I don't feel very strongly about this, but the Typeable example (which drove the entire thread!) is quite compelling to me.

Simon

Last edited 9 months ago by simonpj (previous) (diff)

comment:23 follow-up: Changed 9 months ago by goldfire

I agree with Simon's comment:20 and comment:21. I think those changes are for the better.

As to comment:22, it sounds like you wish to distinguish between INCOHERENTABLE and INCOHERENTING. (I don't want to use those names, just draw the parallel to the OVERLAP pragmas.) Is this a reasonable interpretation of what you're saying?

comment:24 in reply to: ↑ 23 Changed 9 months ago by simonpj

Replying to goldfire:

As to comment:22, it sounds like you wish to distinguish between INCOHERENTABLE and INCOHERENTING. (I don't want to use those names, just draw the parallel to the OVERLAP pragmas.) Is this a reasonable interpretation of what you're saying?

Yes, but I really don't want to actually have that complexity. Obvious alternatives:

  • Current behaviour: INCOHERENT means INCOHERNTING
  • Behaviour proposed in comment:22: INCOHERENT means INCOHERENTABLE
  • Another alternative: INCOHERENT means both INCOHERENTING and INCOHERENTABLE

comment:25 Changed 9 months ago by simonpj

Just for completeness, here is the proposed specification again, with the fixes above, except for the remarks about incoherence in comments 22-24:

The willingness to be overlapped or incoherent is a property of the instance declaration itself, controlled as follows:

 * An instance is "incoherent" 
   if it has an INCOHERENT pragama, or 
   if it appears in a module compiled with -XIncoherentInstances.

 * An instance is "overlappable"
   if it has an OVERLAPPABLE or OVERLAPS pragama, or
   if it appears in a module compiled with -XOverlappingInstances, or
   if the instance is incoherent.

 * An instance is "overlapping"
   if it has an OVERLAPPING or OVERLAPS pragama, or
   if it appears in a module compiled with -XOverlappingInstances, or
   if the instance is incoherent.
     compiled with -XOverlappingInstances.

Now suppose that, in some client module, we are searching for an instance 
of the target constraint (C ty1 .. tyn). The search works like this.

 * Find all instances I that match the target constraint; that is, the
   target constraint is a substitution instance of I. These instance
   declarations are the candidates.

 * Find all non-candidate instances that unify with the target
   constraint. Such non-candidates instances might match when the
   target constraint is further instantiated. If all of them are
   incoherent, proceed; if not, the search fails.

 * Eliminate any candidate IX for which both of the following hold:
   * There is another candidate IY that is strictly more specific;
     that is, IY is a substitution instance of IX but not vice versa.

   * Either IX is overlappable or IY is overlapping.

 * If only one candidate remains, pick it. Otherwise if all remaining
   candidates are incoherent, pick an arbitrary candidate. Otherwise fail.

comment:26 Changed 9 months ago by andreas.abel

Quoting from the spec:

  • Eliminate any candidate IX for which both of the following hold:
    • There is another candidate IY that is strictly more specific; that is, IY is a substitution instance of IX but not vice versa.
  • Either IX is overlappable or IY is overlapping.

Mathematically, this makes a lot of sense. But put on the hat of library writers, and users, and users that don't rtfm. Looking out from under this hat, the one may always wonder whether one should make one's generic instances OVERLAPPABLE or not.

If I create a library with type class Bla and

  instance Bla a => Bla [a]

I could be a nice library writer and spare my users from declaring their Bla String instances as OVERLAPPING, so I'd write

  instance {-# OVERLAPPABLE #-} Bla a => Bla [a]

Or maybe that would be malicious?

I think the current proposal is too sophisticated. There are no convincing examples given in the discussion so far that demonstrate where this sophistication pays off in practice.

Keep in mind that 99% of the Haskell users will never study the instance resolution algorithm or its specification, but just flip on/off pragmas until their code goes through. [At least that was my approach: whenever GHC asks for one more LANGUAGE pragma, just throw it in.]

comment:27 Changed 9 months ago by Simon Peyton Jones <simonpj@…>

In bfaa17998ed0cb8b22132d8e824b274ac5f038cc/ghc:

Add comments about the {-# INCOHERENT #-} for Typeable (f a)

C.f. Trac #9242

comment:28 Changed 9 months ago by Simon Peyton Jones <simonpj@…>

In 1ae5fa451f4f554e0d652d55f9312a585188ce13/ghc:

Complete work on new OVERLAPPABLE/OVERLAPPING pragmas (Trac #9242)

* Deprecate -XOverlappingInstances

* Update test suite.  Several tests even had entirely unnecessary
  uses of -XOverlappingInstances

* Update user manual with a careful description of the instance
  resolution story

* Fix an outright bug in the handling of duplidate instances in GHCi,
  which are meant to silently overwrite the earlier duplicate. The
  logic was right for family instances but was both more complicated,
  and plain wrong, for class instances.  (If you are interested, the
  bug was that we were eliminating the duplicate from the InstEnv, but
  not from the [ClsInst] held in tcg_insts.)  Test is ghci044a.

comment:29 Changed 9 months ago by simonpj

So what remains now is to decide about the behaviour of "incoherent". Here is a possible re-draft:

Suppose that, in some client module, we are searching for an instance 
of the target constraint (C ty1 .. tyn). The search works like this.

 * Find all instances I that match the target constraint; that is, the
   target constraint is a substitution instance of I. These instance
   declarations are the candidates.

 * Eliminate any candidate IX for which both of the following hold:
   * There is another candidate IY that is strictly more specific;
     that is, IY is a substitution instance of IX but not vice versa.

   * Either IX is overlappable or IY is overlapping.

 * If only one candidate remains, pick it. Otherwise if all remaining
   candidates are incoherent, pick an arbitrary candidate. Otherwise fail.

 * If the selected candidate (from the previous step) is not
   incoherent, then find all non-candidate instances that unify
   with the target constraint. Such non-candidates instances
   might match when the target constraint is further
   instantiated. If all of them are incoherent, proceed; if not,
   the search fails.

 * Return the selected candidate.

The difference is that we are slightly more permissive in the case of incoherent instances: if the selected candidate is incoherent, then we ignore all unifying ones.

That would allow us to write

instance {-# INCOHERENT #-} Typeable (n::Nat) where ...
instance (Typeable f, Typeable a) => Typeable (f a) where ...

as was proposed in the original Description of this ticket, rather than making the (f a) instance incoherent.

I think this would affect practically no one, and would accept a few more programs. So I don't think it's very controversial.

Simon

comment:30 Changed 9 months ago by dterei

I'd like to propose we remove the OVERLAPS pragma. Why?

Long Version

https://ghc.haskell.org/trac/ghc/wiki/SafeHaskell/NewOverlappingInstances#NewOverlappingInstances--a.k.aInstanceSpecificPragmas

Short Version (kind-of)

The security implications of OVERLAPPABLE vs. OVERLAPPING are fairly different. Remember, in Safe Haskell we apply a policy of only allowing instances from a module M compiled with -XSafe to overlap other instances from module M. If it overlaps (and is the most specific overlap) instances from modules other than M then we don't allow this to succeed. This is done to ensure that untrusted code compiled with -XSafe can't alter the behavior of existing code, some of which may be part of the TCB and security critical.

Brining the new finer grained pragmas into the story we get the following:

  • OVERLAPPABLE is the programmer communicating that they can be overlapped, an open instance if you will. We want to relax the above restriction and allow instances from -XSafe modules to overlap instances from their own module AND instances declared OVERLAPPABLE that reside in any module.
  • OVERLAPPING is the programming simply declaring they may overlap less specific instances. We want to keep the above restriction for these instances. That is, a instance I1 from a -XSafe module M won't be able to overlap as the most specific instance, a instance I2 from another module if I2 is marked as OVERLAPPING.

This distinction enables new encodings in Safe Haskell by allowing security library authors to distinguish how untrusted code can overlap their instances. In some way giving them open vs closed instances.

This distinction is subtle and important. Having a pragma OVERLAPS that implies both glosses over this and will encourage developers to use this without much thought.

Safe Inference

We can also safely infer a module that only has OVERLAPPABLE instances as safe, while ones that contain OVERLAPPING or OVERLAPS instances must be regarded as unsafe since there is a difference in semantics of these pragmas under Safe vs Unsafe.

So we also have an advantage if developers are more specific about what they want, than just defaulting to OVERLAPS.

Last edited 9 months ago by dterei (previous) (diff)

comment:31 Changed 9 months ago by jrp

The ghc build system itself still uses -XOverlappingInstances and so presumably needs to be updated too.

"rm" -f libraries/haskeline/dist-install/build/.depend-v-dyn-p-dyn.haskell.tmp  
"inplace/bin/ghc-stage1" -M -static  -O -H64m    -package-name haskeline-0.7.1.2 -hide-all-packages -i -ilibraries/haskeline/. -ilibraries/haskeline/dist-install/build -ilibraries/haskeline/dist-install/build/autogen -Ilibraries/haskeline/dist-install/build -Ilibraries/haskeline/dist-install/build/autogen -Ilibraries/haskeline/includes   -optP-DUSE_GHC_ENCODINGS -optP-DTERMINFO -optP-include -optPlibraries/haskeline/dist-install/build/autogen/cabal_macros.h -package base-4.7.1.0 -package bytestring-0.10.4.0 -package containers-0.5.5.1 -package directory-1.2.1.0 -package filepath-1.3.0.2 -package terminfo-0.4.0.0 -package transformers-0.4.1.0 -package unix-2.7.0.2 -Wall -XHaskell98 -XForeignFunctionInterface -XRank2Types -XFlexibleInstances -XTypeSynonymInstances -XFlexibleContexts -XExistentialQuantification -XScopedTypeVariables -XGeneralizedNewtypeDeriving -XMultiParamTypeClasses -XOverlappingInstances -XUndecidableInstances -XCPP -XDeriveDataTypeable -XPatternGuards -O2  -no-user-package-db -rtsopts      -odir libraries/haskeline/dist-install/build -hidir libraries/haskeline/dist-install/build -stubdir libraries/haskeline/dist-install/build -dep-makefile libraries/haskeline/dist-install/build/.depend-v-dyn-p-dyn.haskell.tmp -dep-suffix "" -dep-suffix "dyn_" -dep-suffix "p_" -dep-suffix "dyn_" -include-pkg-deps  libraries/haskeline/./System/Console/Haskeline.hs  libraries/haskeline/./System/Console/Haskeline/Completion.hs  libraries/haskeline/./System/Console/Haskeline/MonadException.hs  libraries/haskeline/./System/Console/Haskeline/History.hs  libraries/haskeline/./System/Console/Haskeline/IO.hs  libraries/haskeline/./System/Console/Haskeline/Backend.hs  libraries/haskeline/./System/Console/Haskeline/Backend/WCWidth.hs  libraries/haskeline/./System/Console/Haskeline/Command.hs  libraries/haskeline/./System/Console/Haskeline/Command/Completion.hs  libraries/haskeline/./System/Console/Haskeline/Command/History.hs  libraries/haskeline/./System/Console/Haskeline/Command/KillRing.hs  libraries/haskeline/dist-install/build/System/Console/Haskeline/Directory.hs  libraries/haskeline/./System/Console/Haskeline/Emacs.hs  libraries/haskeline/./System/Console/Haskeline/InputT.hs  libraries/haskeline/./System/Console/Haskeline/Key.hs  libraries/haskeline/./System/Console/Haskeline/LineState.hs  libraries/haskeline/./System/Console/Haskeline/Monads.hs  libraries/haskeline/./System/Console/Haskeline/Prefs.hs  libraries/haskeline/./System/Console/Haskeline/RunCommand.hs  libraries/haskeline/./System/Console/Haskeline/Term.hs  libraries/haskeline/./System/Console/Haskeline/Command/Undo.hs  libraries/haskeline/./System/Console/Haskeline/Vi.hs  libraries/haskeline/./System/Console/Haskeline/Recover.hs  libraries/haskeline/dist-install/build/System/Console/Haskeline/Backend/Posix.hs  libraries/haskeline/./System/Console/Haskeline/Backend/Posix/Encoder.hs  libraries/haskeline/./System/Console/Haskeline/Backend/DumbTerm.hs  libraries/haskeline/./System/Console/Haskeline/Backend/Terminfo.hs

on the commandline: Warning:
    -XOverlappingInstances is deprecated: instead use per-instance pragmas OVERLAPPING/OVERLAPPABLE/OVERLAPS

libraries/base/Data/OldTypeable.hs:4:14: Warning:
    -XOverlappingInstances is deprecated: instead use per-instance pragmas OVERLAPPING/OVERLAPPABLE/OVERLAPS

libraries/base/Data/OldTypeable/Internal.hs:18:14: Warning:
    -XOverlappingInstances is deprecated: instead use per-instance pragmas OVERLAPPING/OVERLAPPABLE/OVERLAPS

libraries/base/Data/Typeable.hs:3:14: Warning:
    -XOverlappingInstances is deprecated: instead use per-instance pragmas OVERLAPPING/OVERLAPPABLE/OVERLAPS

libraries/base/Data/Typeable/Internal.hs:18:14: Warning:
    -XOverlappingInstances is deprecated: instead use per-instance pragmas OVERLAPPING/OVERLAPPABLE/OVERLAPS

Last edited 9 months ago by jrp (previous) (diff)

comment:32 Changed 9 months ago by simonpj

David, I don't think it's feasible to remove OVERLAPS at the moment, unless you can amass some real support for it.

  • Main reason: people have argued precisely the opposite: that OVERLAPPING and OVERLAPPABLE are too complicated, and they should be removed in favour of just OVERLAPS. In principle I like the idea of forcing people to be explicit, but I think it's bridge too far.
  • Tiresome reason: the parser isn't up to parsing two pragmas; currently {-# OVERLAPPING, OVERLAPPABLE #-} isn't allowed. Someone would have to fix that.

Of course, if -XSafe is on, you can modify the behaviour if you like. So if you want to propose something along those lines, go ahead.

Simon

comment:33 Changed 9 months ago by dterei

I noticed Andreas Abel on the mailing list objected to removing OVERLAPS but otherwise felt people were silent on the issue. We don't want to just make a chance in -XSafe as ideally -XSafe and GHC Haskell are as close as possible, otherwise the long term viability of Safe Haskell isn't good.

I'll see what support I can rally or alternatives come up.

comment:34 Changed 9 months ago by carter

I'd be willing to do the leg work for augmenting the pragma parser, since I need to get familiar with the pragma parser anyways for some of my own work. (if of course, that would enable a desired design choice, if not that don't mind me)

comment:35 Changed 9 months ago by Simon Peyton Jones <simonpj@…>

In dff0623d5ab13222c06b3ff6b32793e05b417970/ghc:

Implement the final change to INCOHERENT from Trac #9242

The change here is to make INCOHERENT slightly more permissive:

  if the selected candidate is incoherent
  then ignore all unifying candidates

This allows us to move the {-# INCOHERENT #-} pragma from
  from   instance Typeable (f a)
  to     Typeable (n:Nat) and Typable (s:Symbol)
where it belongs, and where Trac #9242 said it should be.

I don't think this will affect anyone.

I've updated the user manual.

comment:36 Changed 9 months ago by simonpj

  • Resolution set to fixed
  • Status changed from new to closed

I have finally done the INCOHERENT thing.

I'll close this ticket. There are some loose ends which I'll list here:

  • David, if you want to open a new ticket for OVERLAPS, by all means do so, if you think you've got a good proposal with user support.
  • I'd like to make -XUndecideableInstances a per-instance pragma, instead of a module-wide LANGUAGE flag. That would be consistent with this OVERLAPPABLE stuff. Anyone want to do that?
  • At least one person suggested that there should be a LANGUAGE pragma that switches on recognition of the per-instance pragmas. So {-# OVERLAPPABLE #-} is only honoured if you have -XHonourOverlappingInstnaces or something. Personally I think this is too much, but users may differ!

Simon

comment:37 Changed 6 months ago by hvr

Fyi (& hoping you may give it a look) somewhat related to this ticket is Phab:D377 which is about removing -XOverlappingInstances from Data.Typeable.*

comment:38 Changed 6 months ago by Herbert Valerio Riedel <hvr@…>

In cbb20ab2c3222da75625bcf41f8ff67a7e9ba5f7/ghc:

Drop deprecated `OverlappingInstances` from base

With #9242 the `OverlappingInstances` extension got deprecated, this
commit adapts the only two remaining places in `base` where it was still
used.

Starting with this commit, the `Typeable (s t)` instance (which seemingly
was the motivation for using `OverlappingInstances` in the first place
when `Typeable` was neither polykinded nor auto-derived-only, see also
commit ce3fd0e02826367e6134a3362d8d37aa114236f5 which introduced
overlapping instances) does no longer allow overlapping instances, and
there doesn't seem to be any good reason to keep allowing overlapping
instance now.

This also removes redundant `LANGUAGE`/`OPTIONS_GHC` pragmas from
`Data.Typeable` and refactors the language pragmas into more uniform
single-line pragmas.

Reviewed By: austin

Differential Revision: https://phabricator.haskell.org/D377
Note: See TracTickets for help on using tickets.