-Wsimplifiable-class-constraints is overzealous in generating warnings
Recent GHC versions warn about simplifiable-class-constraints. I assume the warnings are intended to deal with cases where users write code such as
f :: Ord [a] => [a] -> [a] -> Bool
instead of
f :: Ord a => [a] -> [a] -> Bool
In such cases, the class constraint is indeed simplifiable.
However, there is a perfectly good use case where these warnings are unnecessary and counterproductive. The use case is when we use a type class as an abbreviation for a (potentially complicated) type constraint.
For example, suppose we have written a library that provides a bunch of functionality that is divided into many type classes. We can abstract a number of constraints into a single type class by doing the following:
class (Red a, Green a, Blue a, Yellow a) => Multicolored a
instance (Red a, Green a, Blue a, Yellow a) => Multicolored a
Now both the library and the user can write functions using the Multicolored type class:
f :: Multicolored a => ....
which will be perfectly equivalent to
f :: (Red a, Green a, Blue a, Yellow a) => ...
Please note that this is a form of abstraction, which is generally considered to be a good thing in functional programming. For example, the classes Red, Green, etc., may be internal to the library and may not be intended to be exposed to the user. Maybe the implementation of the Multicolored type class will change in the future. Maybe new colors will be added to the library.
The problem is that GHC will now issue simplifiable-class-constraints warnings for this code, not just when the library is compiled, but each time the library is used. GHC seems to be under the impression that the type constraint Multicolored a
should be "simplified" to (Red a, Green a, Blue a, Yellow a)
every time it is used. The generated warning message says something about type inference being "fragile" and about using "MonoLocalBinds", which I am pretty sure does not apply in this situation.
For a real-world example, consider the QData class in Quipper. It is one of the main type classes in Quipper, containing any types that can be used as "quantum data". It is defined from more primitive concepts like this:
class (qa ~ QType (CType qa),
qa ~ QTypeB (BType qa),
qa ~ QCType Qubit Bool qa,
qa ~ QType qa,
QCData qa,
QCData (CType qa)
) => QData qa
instance (qa ~ QType (CType qa),
qa ~ QTypeB (BType qa),
qa ~ QCType Qubit Bool qa,
qa ~ QType qa,
QCData qa,
QCData (CType qa)
) => QData qa
Note that there is only a single instance, so the typeclass QData qa
acts as an abbreviation for a whole bunch of constraints. It involves several type families (for example, QType and QCType are type families) and several equality constraints. This kind of magic is essential to Quipper, and it makes full use of the power and magic of Haskell type inference.
The problem is that GHC now generates a simplifiable-class-constraints warning every time a user uses the QData type class, when clearly it would not be a simplification to replace QData by its definition.
Please consider doing one of the following (in decreasing order of my preference):
-
Refine simplifiable-class-constraints to warn only if a type class is applied to a composite type (e.g. Ord [a]), and not when it is applied to a variable (e.g. QData a);
-
Provide a different mechanism for defining type class abbreviations, instead of the above single-class-single-instance trick;
-
Provide a way to turn off the simplifiable-class-constraints warnings in the module where the type class is defined. (Currently it must be turned off in each module where the type class is used).
Thanks!