13
votes

I know of the documentation for -XUndecidableInstances, but I thought I'd ask for an elaboration.

Suppose I have two multi-parameter typeclasses (allowed with -XMultiParamTypeClasses)

class Foo a b
class Goo a b

Now, suppose I have a parameterized data type

data Bar a b

which I want to make an instance of Foo when one of its parameters is part of an instance of Goo. I'm not sure the previous sentence uses exact terminology, so here's what I want to write:

instance (Goo c d) => Foo d (Bar a d)

I'm not allowed to without the UndecidableInstances extension. Am I correct in thinking this is because the instance doesn't refer to the c type?

Should I...

  1. Just enable the extension? Can somebody elaborate on what kinds of trouble it can get me into?
  2. Add another parameter to Foo, so that the last instance declaration becomes something like Foo c d (Bar a d)? A problem with this is that I might have other instances of Foo that never make any reference to any such "fourth type parameter" (i.e. there are instances of the form instance Foo A B in unrelated parts of my code), so these would break. I'd rather fix my instance, not my class.
  3. Create a new class FooGoo with enough parameters? I would feel like I'm repeating myself in that case, but at least I wouldn't break unrelated classes.

Does anyone have any words of wisdom?

1

1 Answers

10
votes

Am I correct in thinking this is because the instance doesn't refer to the c type?

Yes, your code does not adhere to (from here):

For each assertion in the context: No type variable has more occurrences in the assertion than in the head

In general, you should be safe unless you add other instances that would, together, form a loop. Things only get really hairy (and compiler-dependent) when it comes to OverlappingInstances, and rightout evil when you go IncoherentInstances.

Without knowing more about what you're trying to accomplish it's hard to give sound design advice, but the first thing to check is whether you really, really need to have c as a parameter to Goo. You might be able to do express what you want to accomplish like this:

class Goo d where
    bar :: d c -> Int
    baz :: Quux c => d c -> Int