Skip to content

Commit ae93ef4

Browse files
committed
Special-case Eql and support single param type classes
1 parent 19c5d65 commit ae93ef4

File tree

3 files changed

+105
-58
lines changed

3 files changed

+105
-58
lines changed

compiler/src/dotty/tools/dotc/typer/Deriving.scala

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -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

tests/run/derive-multi.scala

Lines changed: 0 additions & 42 deletions
This file was deleted.

tests/run/multi-param-derives.scala

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import scala.deriving._
2+
3+
object Test extends App {
4+
{
5+
trait Show[T]
6+
object Show {
7+
given as Show[Int] {}
8+
given [T] as Show[Tuple1[T]] given (st: Show[T]) {}
9+
given [T, U] as Show[(T, U)] given (st: Show[T], su: Show[U]) {}
10+
11+
def derived[T] given (m: Mirror.Of[T], r: Show[m.MirroredElemTypes]): Show[T] = new Show[T] {}
12+
}
13+
14+
case class Mono(i: Int) derives Show
15+
case class Poly[A](a: A) derives Show
16+
//case class Poly11[F[_]](fi: F[Int]) derives Show
17+
case class Poly2[A, B](a: A, b: B) derives Show
18+
}
19+
20+
{
21+
trait Functor[F[_]]
22+
object Functor {
23+
def derived[F[_]] given (m: Mirror { type MirroredType = F }): Functor[F] = new Functor[F] {}
24+
}
25+
26+
//case class Mono(i: Int) derives Functor
27+
case class Poly[A](a: A) derives Functor
28+
//case class Poly11[F[_]](fi: F[Int]) derives Functor
29+
//case class Poly2[A, B](a: A, b: B) derives Functor
30+
}
31+
32+
{
33+
trait FunctorK[F[_[_]]]
34+
object FunctorK {
35+
def derived[F[_[_]]] given (m: Mirror { type MirroredType = F }): FunctorK[F] = new FunctorK[F] {}
36+
}
37+
38+
//case class Mono(i: Int) derives FunctorK
39+
//case class Poly[A](a: A) derives FunctorK
40+
case class Poly11[F[_]](fi: F[Int]) derives FunctorK
41+
//case class Poly2[A, B](a: A, b: B) derives FunctorK
42+
}
43+
44+
{
45+
trait Bifunctor[F[_, _]]
46+
object Bifunctor {
47+
def derived[F[_, _]] given (m: Mirror { type MirroredType = F }): Bifunctor[F] = ???
48+
}
49+
50+
//case class Mono(i: Int) derives Bifunctor
51+
//case class Poly[A](a: A) derives Bifunctor
52+
//case class Poly11[F[_]](fi: F[Int]) derives Bifunctor
53+
case class Poly2[A, B](a: A, b: B) derives Bifunctor
54+
}
55+
}

0 commit comments

Comments
 (0)