@@ -97,48 +97,82 @@ trait Deriving { this: Typer =>
97
97
val resultType = derivedType.appliedTo(clsTpe)
98
98
val instanceInfo = ExprType (resultType)
99
99
addDerivedInstance(originalType.typeSymbol.name, instanceInfo, derived.sourcePos)
100
- } else {
101
- // A matrix of all parameter combinations of current class parameters
102
- // and derived typeclass parameters.
103
- // Rows: parameters of current class
104
- // Columns: parameters of typeclass
105
-
106
- // Running example: typeclass: class TC[X, Y, Z], deriving class: class A[T, U]
100
+ } else if (typeClass == defn.EqlClass ) {
101
+ // Special case derives semantics for the Eql type class
102
+
103
+ // Assumptions:
104
+ // 1. Type params of the deriving class correspond to all and only
105
+ // elements of the deriving class which are relevant to equality (but:
106
+ // type params could be phantom, or the deriving class might have an
107
+ // element of a non-Eql type non-parametrically).
108
+ //
109
+ // 2. Type params of kinds other than * can be assumed to be irrelevant to
110
+ // the derivation (but: eg. Foo[F[_]](fi: F[Int])).
111
+ //
112
+ // Are they reasonable? They cover some important cases (eg. Tuples of all
113
+ // arities). derives Eql is opt-in, so if the semantics don't match those
114
+ // appropriate for the deriving class the author of that class can provide
115
+ // their own instance in the normal way. That being so, the question turns
116
+ // on whether there are enough types which fit these semantics for the
117
+ // feature to pay its way.
118
+
119
+ // Procedure:
120
+ // We construct a two column matrix of the deriving class type parameters
121
+ // and the Eql typeclass parameters.
122
+ //
123
+ // Rows: parameters of the deriving class
124
+ // Columns: parameters of the Eql typeclass (L/R)
125
+ //
126
+ // Running example: typeclass: class Eql[L, R], deriving class: class A[T, U, V]
107
127
// clsParamss =
108
- // T_X T_Y T_Z
109
- // U_X U_Y U_Z
128
+ // T_L T_R
129
+ // U_L U_R
130
+ // V_L V_R
110
131
val clsParamss : List [List [TypeSymbol ]] = cls.typeParams.map { tparam =>
111
- if (nparams == 0 ) Nil
112
- else if (nparams == 1 ) tparam :: Nil
113
- else typeClass.typeParams.map(tcparam =>
132
+ typeClass.typeParams.map(tcparam =>
114
133
tparam.copy(name = s " ${tparam.name}_ $$ _ ${tcparam.name}" .toTypeName)
115
134
.asInstanceOf [TypeSymbol ])
116
135
}
136
+ // Retain only rows with L/R params of kind * which Eql can be applied to.
137
+ // No pairwise evidence will be required for params of other kinds.
117
138
val firstKindedParamss = clsParamss.filter {
118
139
case param :: _ => ! param.info.isLambdaSub
119
- case nil => false
140
+ case _ => false
120
141
}
121
142
122
143
// The types of the required evidence parameters. In the running example:
123
- // TC[T_X, T_Y, T_Z ], TC[U_X, U_Y, U_Z ]
144
+ // Eql[T_L, T_R ], Eql[U_L, U_R], Eql[V_L, V_R ]
124
145
val evidenceParamInfos =
125
146
for (row <- firstKindedParamss)
126
147
yield derivedType.appliedTo(row.map(_.typeRef))
127
148
128
149
// The class instances in the result type. Running example:
129
- // A[T_X, U_X], A[T_Y, U_Y ], A[T_Z, U_Z ]
150
+ // A[T_L, U_L, V_L ], A[T_R, U_R, V_R ]
130
151
val resultInstances =
131
152
for (n <- List .range(0 , nparams))
132
153
yield cls.typeRef.appliedTo(clsParamss.map(row => row(n).typeRef))
133
154
134
- // TC [A[T_X, U_X], A[T_Y, U_Y ], A[T_Z, U_Z ]]
155
+ // Eql [A[T_L, U_L, V_L ], A[T_R, U_R, V_R ]]
135
156
val resultType = derivedType.appliedTo(resultInstances)
136
157
137
158
val clsParams : List [TypeSymbol ] = clsParamss.flatten
138
159
val instanceInfo =
139
160
if (clsParams.isEmpty) ExprType (resultType)
140
161
else PolyType .fromParams(clsParams, ImplicitMethodType (evidenceParamInfos, resultType))
141
162
addDerivedInstance(originalType.typeSymbol.name, instanceInfo, derived.sourcePos)
163
+ } else if (nparams == 1 && ! typeClass.typeParams.head.info.isLambdaSub && ! cls.typeParams.exists(_.info.isLambdaSub)) {
164
+ val clsParams : List [TypeSymbol ] = cls.typeParams
165
+ val evidenceParamInfos = clsParams.map(param => derivedType.appliedTo(param.typeRef))
166
+ val resultInstance = cls.typeRef.appliedTo(clsParams.map(_.typeRef))
167
+ val resultType = derivedType.appliedTo(resultInstance)
168
+ val instanceInfo =
169
+ if (clsParams.isEmpty) ExprType (resultType)
170
+ else PolyType .fromParams(clsParams, ImplicitMethodType (evidenceParamInfos, resultType))
171
+ addDerivedInstance(originalType.typeSymbol.name, instanceInfo, derived.sourcePos)
172
+ } else if (nparams == 0 ) {
173
+ ctx.error(i " type ${typeClass.name} in derives clause of ${cls.name} has no type parameters " , derived.sourcePos)
174
+ } else {
175
+ ctx.error(i " ${cls.name} cannot be unified with the type argument of ${typeClass.name}" , derived.sourcePos)
142
176
}
143
177
}
144
178
0 commit comments