Comparing class alias proposals
John Meacham's proposal for type class aliases has been around for a few years now. It has not been implemented yet; instead, several variations to his original proposal have come up. In this document we collect those ideas and their motivations in a hopefully clear overview. We also point out potential pitfalls or things that need more pondering: many ideas look very attractive and promising but prove troublesome when made formal. We believe this is the main reason they haven't been implemented yet.
For each idea we give a motivation from the point of view of a stakeholder. We identify three stakeholders:
- the designer of a class or class hierarchy;
- the author of class instances;
- the programmer using the classes and instances.
Furthermore, most motivations can be grouped into two categories:
- increasing the flexibility of the class system, useful when designing new API;
- changing an existing API while maintaining backwards compatibility.
1 List of proposals
We've found the following documents that propose extensions or variations of the original proposal:
- John Meacham's original proposal
- Context alias
- Superclass defaults
- Class system extension proposal
- GHC ticket 788: Class aliases
2 Context synonyms
The most basic idea is that of a context synonym (CS):
context BoundedEnum a = (Bounded a, Enum a) context SatisfyA f s = (Satisfy f, Alternative f, Input f ~ s)
A context synonym may appear anywhere a context is allowed:
- in the type signature of a function;
- in a data type or constructor;
- in a superclass constraint.
The arguments to a CS have to be kind-checked. In the examples above:
Bounded, Enum, BoundedEnum :: * -> Context Satisfy, Alternative :: (* -> *) -> Context Input :: (* -> *) -> * SatisfyA :: (* -> *) -> * -> Context
Context synonyms are synonymous and interchangable with their right-hand sides, much like type synonyms. Specifically, they do not define any new classes (dictionaries) or functions. Ideally, they can be exported and documented, just like type synonyms.
Motivation: CS's are useful when a particularly long context keeps popping up, something that often happens when type-level programming is applied, for example with generic programming. They are interesting for programmers using already existing classes.
3 Higher-order context synonyms
Possible additions to the context synonyms include:
May a CS's RHS refer to other CS's? For example:
context Foo a = (BoundedEnum a, Eq a, Ord a)
This introduces the possibility of loops. Should loops be forbidden? It could be argued that in some cases of loops, the CS's transitive closure should be used, but this is only acceptable if the closure is finite. It seems more elegant to just forbid all loops.
Apart from type constructors, may a CS's variables be class constructors (things of kind ... -> Context) as well? For example:
context Bar a = a Int
Here Bar :: (* -> Context) -> Context
May CS's be passed as arguments to other CS's? For example:
context Biz = Bar BoundedEnum
May CS's be partially applied? Consider these alternate definitions of BoundedEnum:
context BoundedEnum a = (Bounded, Enum) a context BoundedEnum = (Bounded, Enum)
This would overload the
, separator to accept not just things of kind * as operands, but things of any kind, as long as the kinds of both operands are equal.
Motivation: these ideas are direct extensions of context synonyms and are useful for clients.
4 Instances of class aliases
One of John Meacham's main reasons for his proposal is the lack of possibilities for abstraction in the current class system: "there is no way to hide the fact that you changed a class hierarchy", he writes. Therefore, he introduces the possibility of writing instances of class aliases:
class alias Num a = (Additive a, AdditiveNegation a, Multiplicative a, FromInteger a) instance Num a where zero = ... (+) = ... (-) = ... negate = ... one = ... (*) = ... fromInteger = ...
Motivation: Class designers can now later split up a class in smaller classes if they redefine the initial class as a class alias for the new, smaller type classes. More specifically, this could solve the Num hierarchy problem: how to redefine the Num-related type classes in the prelude while maintaining backwards compatibility?
John's example above introduces new functions
one which conflicts with the idea of splitting up Num in strict subclasses. However, this problem is (supposedly?) solved by overriding superclass defaults; see below. Also,
abs are not present in the example, but they may be added to the appropriate subclasses.
John's proposal can be decomposed into two smaller steps:
- being able to write one instance header for several instances at the same time
- then replacing the several type classes by a class alias.
4.1 Class alias or context synonym?
Can class aliases as used above be read as "context synonyms"? Certainly not without noting that context synonyms may include equality constraints, which make no sense when writing instances of class aliases. If class aliases are to piggyback on context synonyms, then we need to think carefully about what form such a context synonym should have for instances to make sense. Equality constraints are most likely not the only difference.