48

I have a multi-parameter typeclass with a functional dependency:

class Multi a b | a -> b

I also have a simple, non-injective type synonym family:

type family Fam a

I want to write an instance of Multi that uses Fam in the second parameter, like this:

instance Multi (Maybe a) (Fam a)

However, this instance is not accepted. GHC complains with the following error message:

error:
  • Illegal type synonym family application in instance: Fam a
  • In the instance declaration for ‘Multi (Maybe a) (Fam a)’

Fortunately, there is a workaround. I can perform a usual trick for moving a type out of the instance head and into an equality constraint:

instance (b ~ Fam a) => Multi (Maybe a) b

This instance is accepted! However, I got to thinking, and I started to wonder why this transformation could not be applied to all instances of Multi. After all, doesn’t the functional dependency imply that there can only be one b per a, anyway? In this situation, it seems like there is no reason that GHC should reject my first instance.

I found GHC Trac ticket #3485, which describes a similar situation, but that typeclass did not involve a functional dependency, so the ticket was (rightfully) closed as invalid. In my situation, however, I think the functional dependency avoids the problem described in the ticket. Is there anything I’m overlooking, or is this really an oversight/missing feature in GHC?

Alexis King
  • 43,109
  • 15
  • 131
  • 205
  • 2
    It'd be nice to have a self-contained example to be able to explore hypotheses when trying to answer your question. – gallais Jul 27 '17 at 21:14
  • 1
    @gallais Do you mean a self-contained practical example? The code in my question, short as it may be, should compile. – Alexis King Jul 27 '17 at 21:15
  • 1
    I think you've got it right. Ordinarily your second instance would be at risk of overlapping but here the fundep saves you, as you point out. I suspect the reason it hasn't been implemented in GHC is because one could argue that it's a counterintuitive/unexpected feature, which is only useful in specific situations, and which has a straightforward workaround. (To get a definitive answer you'd have to ask a GHC developer, though.) – Benjamin Hodgson Jul 27 '17 at 21:34
  • 3
    I’ve opened [GHC ticket #14046](https://ghc.haskell.org/trac/ghc/ticket/14046) to track this issue. I’ll probably delete this question if the answer is simply “because it isn’t implemented that way”. – Alexis King Jul 27 '17 at 21:59
  • 10
    Please don't delete the question for such a reason. – dfeuer Jul 27 '17 at 22:50
  • Also, some people would really like to make stuff like that actually work. The main impediments have to do with evil overlap and non-termination. – dfeuer Jul 27 '17 at 22:53
  • @dfeuer I’m not sure this question can be answer all that usefully if there really isn’t anything wrong with my suggestion, and this question has accumulated a downvote and a close vote. Anyway, IIUC, “evil overlap” isn’t a problem here due to the fundep, and “non-termination” is already allowed via `UndecideableInstances`, so I’m not sure why that would be any different here, either. – Alexis King Jul 27 '17 at 23:36
  • You have a functional dependency `a -> b`, but I don't find evidence that `(Maybe a) -> (Fam a)`. Is that part of the culprit? – Miao ZhiCheng Feb 19 '23 at 10:42

0 Answers0