Restrict/generalize derives clauses #6961
Merged
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This PR reworks the earlier support for
derives
clauses for multi-parameter type classes (TL;DR it restricts MPTCderives
toEql
whilst preserving the generation of givens for single parameter type classes and ADTs with type parameters of kind*
) and generalizes the handling of single-parameter type classes to support (un)currying of the instances of type classes for ADTs which have type parameters of matching kinds, but possibly more or fewer (illustrative examples below).The handling of
derives Eql
has been left largely unchanged apart from some slight simplifications due to it now only handlingEql
.Note that all cases which don't fall into one of the categories described below are rejected with an error message reporting that the deriving ADT cannot be unified with type argument(s) of the derived type class. Note that zero parameter type classes are now also rejected.
Special case:
derives
semantics for theEql
type classThis has been extracted from the earlier more general multi-parameter type class model. Modulo the assumptions below, the implied semantics are reasonable defaults for
Eql
.Assumptions
Type parameters of the deriving class correspond to all and only the elements of the deriving class which are relevant to equality (caveat: type params could be phantom, or the deriving class might have an element of a non-
Eql
type non-parametrically).Type parameters of kinds other than
*
can be assumed to be irrelevant to the derivation (caveat: consider ADTs of the formFoo[F[_]](fi: F[Int])
).Are these assumptions reasonable? They cover some important cases (eg.
Tuples
of all arities).derives Eql
is opt-in, so if the semantics don't match those appropriate for the deriving class the author of that class can provide their own instance in the normal way. That being so, the question turns on whether there are enough types which fit these semantics for the feature to pay its way.Note also that because
Eql
is a marker trait there are no methods to be implemented in the derived instance ... mere existence is enough.Procedure
Example:
Eql[L, R]
with deriving class: classA[T, U]
.We will be constructing an instance for
Eql[A[T_L, U_L], A[T_R, U_R]]
. We do this by constructing a polymorphic instance with pairwise givens forEql
on the elements on the left and right,Note that instances will be polymorphic iff the ADT is polymorphic.
Single parameter type classes
"natural" instances
A type class instance is called "natural" if the kind of the ADT exactly matches the kind of the type class's single type parameter. Examples,
Note that all "natural" instances will be monomorphic.
(un)curried instances
ADTs which have more or fewer type parameters than the type class's single type parameter are adapted to the type class's shape by the insertion of a type lambda and (if the ADT has more type parameters, the instance method will be polymorphic). The ADT and type class parameters must overlap on the right and have the same kinds at the overlap.
The following illustrates the type parameter alignment process,
This typically yields instances of the expected form, eg.
which are the instances you would expect to write by hand for those ADTs (consitent with partial unification in the
Either
case, and a typical "const" instance in theProd0
case).In the implementation, the form of the "natural" instances described in the previous section drop out naturally from the process of aligning type parameters between the ADT and the type class.
Note that instances will never have given arguments and with be polymorphic iff the ADT has more type parameters than the type class argument type.
"lowered" instances with givens
In the case where the type class type parameter and all ADT type parameters are of kind
*
a derives clause generates instances similarly to the way that the previous multi-parameter type class model did and theEql
derivation still does. From an implementation point of view this is mainly a special case extracted from the earlier implementation, and like theEql
derivation it typically produces the instances which are desired.Note that In this case the ADT has at least one type parameter of kind
*
, which means that it is not a natural type for the type class.The derived instance has a type parameter and a given for each of the type parameters of the ADT,