Opened 6 years ago

Closed 6 years ago

#8599 closed bug (fixed)

Deriving in associated data families ignores instance's constraints

Reported by: mojojojo Owned by: goldfire
Priority: normal Milestone:
Component: Documentation Version: 7.6.3
Keywords: Cc:
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: None/Unknown Test Case:
Blocked By: Blocking:
Related Tickets: Differential Rev(s):
Wiki Page:

Description

This code

class Class a where
  data DF a

data family DF' a

instance (Eq (DF' a)) => Class a where
  newtype DF a = DF (DF' a) deriving (Eq)

fails with

No instance for (Eq (DF' a))
    arising from the 'deriving' clause of a data type declaration
  Possible fix:
    add an instance declaration for (Eq (DF' a))
    or use a standalone 'deriving instance' declaration,
         so you can specify the instance context yourself
  When deriving the instance for (Eq (DF a))

The following way it compiles fine, however

class Class a where
  data DF a

data family DF' a

instance Class a where
  newtype DF a = DF (DF' a)

deriving instance (Eq (DF' a)) => Eq (DF a)

Change History (5)

comment:1 Changed 6 years ago by mojojojo

Summary: Deriving in associated data familiesDeriving in associated data families ignores instance's constraints

comment:2 Changed 6 years ago by goldfire

Component: CompilerDocumentation

This is by design, though quite non-trivially.

The problem is caused by the confluence of two design decisions:

  1. Associated data/newtype instances do not interact with any specified context to the enclosing class instance.
  1. As the manual says in section 7.5.1, "each constraint in the inferred instance context must consist only of type variables, with no repetitions." In the case at hand, the inferred context would be Eq (DF' a), which does not meet this criterion.

The fact in (1) does not seem to be documented anywhere, which is why I'm relabeling this as a documentation bug. Changing this fact seems hard: do we want the context to be inherited by all data constructors of an associated data instance? How can the context interact with an associated type instance (without substantially changing the way type families work)? To me, the only place that the context should possibly be inherited is in derived instances, as the original report suggests. But, even here, its role would be unclear: is the context "fully-specified", like in a standalone-deriving instance? What if a larger context is necessary? What if a smaller one were sufficient? There are consistent points in this design space, but choosing one and articulating it might be hard, and somewhat arbitrary.

Thoughts? If someone agrees with relabeling this as a documentation bug, I can update the stuff on data families when I return from holiday (Jan. 6).

comment:3 Changed 6 years ago by simonpj

Owner: set to goldfire

I agree with Richard here.

The primary goal of allowing you to declare a type/data family within a class declaration is to make the link explicit and to remind you if you forget to give the instance in an instance declaration of the class. I also love the duality:

  • in a class declaration you give the type signature for any value methods; in the instance you give the actual method.
  • in a class declaration you give the kind signature for any associated types; in the instance you give the actual type definitions.

But really it's not much more than syntactic sugar. You could equally well give the associated type family definitions outside the class, and likewise the instances.

However, as syntactic sugar goes, it's pretty heavy. GHC has a lot of code that deals with associated types!

In any case, that's why I agree with Richard's analysis. I think it'd be a mistake to conflate the context for the class instance declaration with the context for a derived equality instance.

Do clarify the documentation, thank you Richard.

Simon

comment:4 Changed 6 years ago by Richard Eisenberg <eir@…>

In 566ba6fa10ade0af159a7b86f4df706cbd6b4163/ghc:

Fix #8599.

This change is just some documentation around ignoring the context
of an enclosing instance when processing `deriving` clauses of an
associated data instance.

comment:5 Changed 6 years ago by goldfire

Resolution: fixed
Status: newclosed
Note: See TracTickets for help on using tickets.