Ticket #4815 (new feature request)

Opened 18 months ago

Last modified 4 months ago

Instance constraints should be used when deriving on associated data types

Reported by: batterseapower Owned by:
Priority: low Milestone: 7.6.1
Component: Compiler Version: 6.12.3
Keywords: Cc:
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: None/Unknown Difficulty:
Test Case: Blocked By:
Blocking: Related Tickets:

Description

Consider this program:

{-# LANGUAGE TypeFamilies, FlexibleContexts #-}

class Eq (Associated a) => Foo a where
    data Associated a

instance Foo a => Foo (Maybe a) where
    data Associated (Maybe a) = AssociatedMaybe (Associated a)
                              deriving (Eq)

This does not compile, giving this error message:

/Users/mbolingbroke/Junk/Repro.hs:9:40:
    No instance for (Eq (Associated a))
      arising from the 'deriving' clause of a data type declaration
                   at /Users/mbolingbroke/Junk/Repro.hs:9:40-41
    Possible fix:
      add an instance declaration for (Eq (Associated a))
      or use a standalone 'deriving instance' declaration instead,
         so you can specify the instance context yourself
    When deriving the instance for (Eq (Associated (Maybe a)))

However, this is surprising because I clearly state that a is Foo, and hence (Associated a) has an Eq instance by the superclass constraint on Foo.

If I point this out explicitly using standalone deriving it works:

{-# LANGUAGE TypeFamilies, FlexibleContexts #-}
{-# LANGUAGE StandaloneDeriving, FlexibleInstances #-}

class Eq (Associated a) => Foo a where
    data Associated a

instance Foo a => Foo (Maybe a) where
    data Associated (Maybe a) = AssociatedMaybe (Associated a)
--                              deriving (Eq)

deriving instance Foo a => Eq (Associated (Maybe a))

So I think the default behaviour for "deriving" on an associated data family should be to include the constraints from the enclosing instance. For now the workaround is just to use standalone deriving.

Change History

Changed 18 months ago by simonpj

It's not clear that you always want this. Suppose your data decl was

instance Foo a => Foo (Maybe a) where
    data Associated (Maybe a) = AssociatedMaybe a
                              deriving (Eq)

Here, Eq a would be enough. Wouldn't you expect this

instance Eq a => Eq (Associated (Maybe a))

rather than

instance Foo a => Eq (Associated a)

? Maybe standalone deriving is the Right Way, not just a workaround.

Changed 18 months ago by batterseapower

But if you ever have a value of type "Associated (Maybe a)" then you pretty much know that there must be a "Foo a" instance by looking at the "Foo (Maybe a)" instance. So you aren't losing much generality in practice by requiring that stronger constraint, rather than the weaker Eq constraint.

(I haven't tried it, but perhaps it's possible to add new instances to an associated data family outside of the instance itself? In which case the logic above is not valid, but since this is a rather uncommon use case I don't think this should argue against my proposed behaviour change. And anyway if you know the weaker constraint is enough and you *really want* to use it because you're going to extend the data family outside an instance itself for some reason, then you can use standalone deriving with the weaker constraint)

It seems to me that just using the instances' constraints is a reasonable thing to do by default, as it stands the very best change of allowing deriving to work. I personally prefer a more reliable deriving mechanism to one that works less often but when it does, generates more general type class instances.

(BTW, personally I find it slightly surprising that you can even mention the type "Associated a" without mentioning "Foo a" in your type signature, though I can kind of see the reasoning)

Changed 17 months ago by igloo

  • milestone set to 7.2.1

Changed 4 months ago by igloo

  • priority changed from normal to low
  • milestone changed from 7.4.1 to 7.6.1
Note: See TracTickets for help on using tickets.