[GHC] #10532: Unnecessary constraints when using overlapping instances

#10532: Unnecessary constraints when using overlapping instances -------------------------------------+------------------------------------- Reporter: | Owner: MikeIzbicki | Status: new Type: bug | Milestone: Priority: normal | Version: 7.10.1 Component: Compiler | Operating System: Unknown/Multiple (Type checker) | Type of failure: None/Unknown Keywords: | Blocked By: Architecture: | Related Tickets: Unknown/Multiple | Test Case: | Blocking: | Differential Revisions: | -------------------------------------+------------------------------------- The following code compiles just fine: {{{ class Foo a where foo :: a -> Int instance Foo a where foo _ = 5 f :: a -> Int f = foo }}} But if we modify the code to use overlapping instances like so: {{{ class Foo a where foo :: a -> Int instance {-# OVERLAPS #-} Foo Int where foo = id instance {-# OVERLAPPABLE #-} Foo a where foo _ = 5 f:: a -> Int f = foo }}} We get the following compiler message: {{{ Overlapping instances for Foo a arising from a use of ‘foo’ Matching instances: instance [overlappable] Foo a -- Defined at ... instance [overlap ok] Foo Int -- Defined at ... (The choice depends on the instantiation of ‘a’ To pick the first instance above, use IncoherentInstances when compiling the other instance declarations) }}} Including the constraint in the type signature makes the problem go away, but the constraint shouldn't be needed. In most use cases this wouldn't be a big deal, but it's making my type signatures much messier looking than they actually are. Adding IncoherentInstances as the error message suggests gives the instance I don't want. Weirdly, I get different instances depending on whether the constraint is in the type signature or not. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/10532 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#10532: Unnecessary constraints when using overlapping instances -------------------------------------+------------------------------------- Reporter: MikeIzbicki | Owner: Type: bug | Status: new Priority: normal | Milestone: Component: Compiler (Type | Version: 7.10.1 checker) | Keywords: Resolution: | Architecture: Operating System: Unknown/Multiple | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Revisions: -------------------------------------+------------------------------------- Description changed by MikeIzbicki: Old description:
The following code compiles just fine:
{{{ class Foo a where foo :: a -> Int instance Foo a where foo _ = 5
f :: a -> Int f = foo }}}
But if we modify the code to use overlapping instances like so:
{{{ class Foo a where foo :: a -> Int instance {-# OVERLAPS #-} Foo Int where foo = id instance {-# OVERLAPPABLE #-} Foo a where foo _ = 5
f:: a -> Int f = foo }}}
We get the following compiler message: {{{ Overlapping instances for Foo a arising from a use of ‘foo’ Matching instances: instance [overlappable] Foo a -- Defined at ... instance [overlap ok] Foo Int -- Defined at ... (The choice depends on the instantiation of ‘a’ To pick the first instance above, use IncoherentInstances when compiling the other instance declarations) }}}
Including the constraint in the type signature makes the problem go away, but the constraint shouldn't be needed. In most use cases this wouldn't be a big deal, but it's making my type signatures much messier looking than they actually are.
Adding IncoherentInstances as the error message suggests gives the instance I don't want. Weirdly, I get different instances depending on whether the constraint is in the type signature or not.
New description: The following code compiles just fine: {{{ class Foo a where foo :: a -> Int instance Foo a where foo _ = 5 f :: a -> Int f = foo }}} But if we modify the code to use overlapping instances like so: {{{ class Foo a where foo :: a -> Int instance {-# OVERLAPS #-} Foo Int where foo = id instance {-# OVERLAPPABLE #-} Foo a where foo _ = 5 f:: a -> Int f = foo }}} We get the following error message: {{{ Overlapping instances for Foo a arising from a use of ‘foo’ Matching instances: instance [overlappable] Foo a -- Defined at ... instance [overlap ok] Foo Int -- Defined at ... (The choice depends on the instantiation of ‘a’ To pick the first instance above, use IncoherentInstances when compiling the other instance declarations) }}} Including the constraint in the type signature makes the problem go away, but the constraint shouldn't be needed. In most use cases this wouldn't be a big deal, but it's making my type signatures much messier looking than they actually are. Adding IncoherentInstances as the error message suggests gives the instance I don't want. Weirdly, I get different instances depending on whether the constraint is in the type signature or not. -- -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/10532#comment:1 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#10532: Unnecessary constraints when using overlapping instances -------------------------------------+------------------------------------- Reporter: MikeIzbicki | Owner: Type: bug | Status: new Priority: normal | Milestone: Component: Compiler (Type | Version: 7.10.1 checker) | Keywords: Resolution: | Architecture: Operating System: Unknown/Multiple | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Revisions: -------------------------------------+------------------------------------- Comment (by goldfire): This looks like expected behavior to me. In the overlapping instances case, GHC can't know which instance to choose when calling `foo` from `f`. If you want GHC to decide based on whether `a` is `Int` at a particular call site, then that's exactly what adding the constraint to `f`'s type signature means. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/10532#comment:2 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#10532: Unnecessary constraints when using overlapping instances -------------------------------------+------------------------------------- Reporter: MikeIzbicki | Owner: Type: bug | Status: new Priority: normal | Milestone: Component: Compiler (Type | Version: 7.10.1 checker) | Keywords: Resolution: | Architecture: Operating System: Unknown/Multiple | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Revisions: -------------------------------------+------------------------------------- Comment (by MikeIzbicki): I don't understand why this would be expected behavior. GHC lets me remove the constraint when overlapping instances are not involved, but requires the constraint when overlapping instances are involved. Why should there be a difference when overlapping instances gets involved? -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/10532#comment:3 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#10532: Unnecessary constraints when using overlapping instances -------------------------------------+------------------------------------- Reporter: MikeIzbicki | Owner: Type: bug | Status: new Priority: normal | Milestone: Component: Compiler (Type | Version: 7.10.1 checker) | Keywords: Resolution: | Architecture: Operating System: Unknown/Multiple | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Revisions: -------------------------------------+------------------------------------- Comment (by goldfire): Because, without overlapping instances, GHC can make a unique correct choice about what instance of `Foo` to use. With the overlapping instances, there is ambiguity which must be resolved. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/10532#comment:4 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#10532: Unnecessary constraints when using overlapping instances -------------------------------------+------------------------------------- Reporter: MikeIzbicki | Owner: Type: bug | Status: new Priority: normal | Milestone: Component: Compiler (Type | Version: 7.10.1 checker) | Keywords: Resolution: | Architecture: Operating System: Unknown/Multiple | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Revisions: -------------------------------------+------------------------------------- Comment (by rwbarton): In any event, what you seem to want is impossible: `f :: a -> Int` can never dispatch on the type of `a`, even with IncoherentInstances or any other language extension. You really must have the constraint if you want to use it! -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/10532#comment:5 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#10532: Unnecessary constraints when using overlapping instances -------------------------------------+------------------------------------- Reporter: MikeIzbicki | Owner: Type: bug | Status: new Priority: normal | Milestone: Component: Compiler (Type | Version: 7.10.1 checker) | Keywords: Resolution: | Architecture: Operating System: Unknown/Multiple | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Revisions: -------------------------------------+------------------------------------- Comment (by MikeIzbicki): Would something bad happen if GHC had the behavior I expected of automatically inferring the constraint? @rwbarton I don't understand what you mean. If I open the code in ghci I can evaluate the function f on any input whenever ghc actually compiles the code. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/10532#comment:6 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#10532: Unnecessary constraints when using overlapping instances -------------------------------------+------------------------------------- Reporter: MikeIzbicki | Owner: Type: bug | Status: closed Priority: normal | Milestone: Component: Compiler (Type | Version: 7.10.1 checker) | Keywords: Resolution: invalid | Architecture: Operating System: Unknown/Multiple | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Revisions: -------------------------------------+------------------------------------- Changes (by simonpj): * status: new => closed * resolution: => invalid Comment: Consider what happens, in your modified code with overlapping instances, when you call `f` thus: `f (4::Int)`. What result do you expect? You might say: 1. The instance `Foo Int` clearly says that `foo` is the identity function on `Int`. I'm giving it an `Int` so `f 4` should return `4`. 2. Or you might say: when I compiled the code for `f`, GHC had to choose which instance to use, of the two available. Since the constraint is `Foo a`, and GHC knows nothing about `a` it must choose the `Foo a` instance. Result, `f 4` returns 5. This incoherence is what GHC is complaining about. If it can't make a unique choice, it decides not to decide, and rejects the program instead. If it silently chose (2), which I think you intend, then if you replaced the call `(f 4)` with the result of inlining `f`, thus `(foo 4)`, the result of the program would change. This has been GHC's behaviour (and Hugs) ever since we introduced overlapping instances. The flag `-XIncoherentInstances` switches off this behaviour, and does (2). Simon -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/10532#comment:7 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#10532: Unnecessary constraints when using overlapping instances -------------------------------------+------------------------------------- Reporter: MikeIzbicki | Owner: Type: bug | Status: closed Priority: normal | Milestone: Component: Compiler (Type | Version: 7.10.1 checker) | Keywords: Resolution: invalid | Architecture: Operating System: Unknown/Multiple | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Revisions: -------------------------------------+------------------------------------- Comment (by MikeIzbicki): I understand the differences between `-XOverlappingInstances` and `-XIncoherentInstances`, and my question is not about that. My question is just about syntax. Without `-XOverlappingInstances`, GHC interprets the types `a -> Int` and `Foo a => a -> Int` as the same thing. Whenever you can use one, you can also use the other (at least in all the cases I tried). But without overlapping instances, GHC no longer interprets these types the same way. Only the second type is valid for `f`, but I would rather write the first type because I know GHC can find an instance `Foo` for all `a`. The particular instance will depend on the weird interactions caused by overlapping instances, but there's guaranteed to be an instance available. My question is: Why is this syntactic difference useful? Maybe parametricity would be broken without it? ---- Even if this syntactic difference is useful, I still think there is a GHC bug. Consider what happens in the overlapping instances case when you omit the type signature for `f = foo`. GHC infers the type `a -> Int`, but then complains that the class constraint `Foo` can't be found. GHC should instead infer the type `Foo a => a -> Int`, which would let us omit the type signature. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/10532#comment:8 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#10532: Unnecessary constraints when using overlapping instances -------------------------------------+------------------------------------- Reporter: MikeIzbicki | Owner: Type: bug | Status: closed Priority: normal | Milestone: Component: Compiler (Type | Version: 7.10.1 checker) | Keywords: Resolution: invalid | Architecture: Operating System: Unknown/Multiple | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Revisions: -------------------------------------+------------------------------------- Comment (by goldfire): What would you like to happen when GHC sees `(f (3 :: Int))`? My guess is that you expect GHC to notice that there is an instance for `Foo Int` around and to use it. But if `f` had the type `a -> Int`, then there is nothing to tell GHC to go out and look for the instance. When we have `f :: Foo a => a -> Int`, it tells GHC to look for a `Foo` instance at call sites. If, on the other hand, you want `f` always to use the `Foo a` instance, then `IncoherentInstances` is the way to go. I don't see this as an issue of parametricity, but just one of a misunderstanding about what the type signature means. As for what happens when you omit the signature: You're hitting the monomorphism restriction. When I load this into GHCi: {{{ {-# LANGUAGE FlexibleInstances, NoMonomorphismRestriction #-} class Foo a where foo :: a -> Int instance {-# OVERLAPS #-} Foo Int where foo = id instance {-# OVERLAPPABLE #-} Foo a where foo _ = 5 f = foo }}} I get an inferred type for `f` of `Foo a => a -> Int`. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/10532#comment:9 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler

#10532: Unnecessary constraints when using overlapping instances -------------------------------------+------------------------------------- Reporter: MikeIzbicki | Owner: Type: bug | Status: closed Priority: normal | Milestone: Component: Compiler (Type | Version: 7.10.1 checker) | Keywords: Resolution: invalid | Architecture: Operating System: Unknown/Multiple | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Revisions: -------------------------------------+------------------------------------- Comment (by MikeIzbicki): Ok. I think I understand. Thanks for all the help! -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/10532#comment:10 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler
participants (1)
-
GHC