@@ -97,48 +97,82 @@ trait Deriving { this: Typer =>
9797 val resultType = derivedType.appliedTo(clsTpe)
9898 val instanceInfo = ExprType (resultType)
9999 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]
107127 // 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
110131 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 =>
114133 tparam.copy(name = s " ${tparam.name}_ $$ _ ${tcparam.name}" .toTypeName)
115134 .asInstanceOf [TypeSymbol ])
116135 }
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.
117138 val firstKindedParamss = clsParamss.filter {
118139 case param :: _ => ! param.info.isLambdaSub
119- case nil => false
140+ case _ => false
120141 }
121142
122143 // 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 ]
124145 val evidenceParamInfos =
125146 for (row <- firstKindedParamss)
126147 yield derivedType.appliedTo(row.map(_.typeRef))
127148
128149 // 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 ]
130151 val resultInstances =
131152 for (n <- List .range(0 , nparams))
132153 yield cls.typeRef.appliedTo(clsParamss.map(row => row(n).typeRef))
133154
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 ]]
135156 val resultType = derivedType.appliedTo(resultInstances)
136157
137158 val clsParams : List [TypeSymbol ] = clsParamss.flatten
138159 val instanceInfo =
139160 if (clsParams.isEmpty) ExprType (resultType)
140161 else PolyType .fromParams(clsParams, ImplicitMethodType (evidenceParamInfos, resultType))
141162 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)
142176 }
143177 }
144178
0 commit comments