Description
I have opened this issue to collect ideas and requirements how we want to evolve the typeclass derivation framework in https://dotty.epfl.ch/docs/reference/contextual/derivation.html. The goal is to come up with something lightweight that can be used as a basis for @milessabin's (and possibly other's) designs for high-level typeclass derivation, and that can also stand on its own as a low-level derivation API.
At the SIP retreat, Miles presented his current design. It included a set of low-level "erased" abstractions that are implemented by compiler-generated code. These abstractions are quite similar in scope to what's supported by Generic
and Mirror
in the current implementation. But there are also differences. (I am writing this down as I recounted it from the SIP retreat, please correct where I am inaccurate).
Differences
-
The erased API is typeless, having
Any
for all inputs and outputs. By contrast, the current API does expose types to some degree even though it uses casts internally. This difference has probably to do with the fact that the erased API was intended for internal use only. -
The erased API does not cover labels. Labels are treated only at the type level.
-
Erased API implementations are meant to be generated automatically for all case classes, case objects, and sealed classes and traits (and we'd have to extend that to all enum values as well). By contrast the current implementation generates a
Generic
instance only if there is aderives
clause. This is not a hard restriction sinceGeneric
instances can be generated also after the fact if they are summoned as an implied instance. But that second way of doing it can lead to code duplication. -
The erased API distinguishes between sums and products. In an ADT each case gets its own variant of
Generic
and theGeneric
for the overall sum type exposes a way to navigate to theGeneric
of the correct case. By contrast the currently implemented API exposes a singleGeneric
for the whole ADT.
Discussion
Here are some comments on each of these four differences.
-
it's probably uncontentious that a published API should expose fine-grained types where possible, so that user's programs don't have to do
asInstanceOf
everywhere. -
Labels should be treated only at the type level. At run-time, we can rely already on
getClass.getName
andproductElementNames
, so no new functionality is needed. -
It would be great if we could generate derivation infrastructure unconditionally for all sealed sum types (including enums), all case classes and objects and all enum values. The constraint to make this feasible is size of the generated code. The additional code we generate for a case class should be modest. In particular, it would be good if no additional class was generated. A case class already generates two JVM classes, one for the class itself and the other for its companion object. It would be problematic to unconditionally generate more classes that serve type class derivation. Also, the additional overhead to support an enum value should be close to zero. The current implementation does not fulfil this requirement since each generated
Generic
is its own class, so generating them unconditionally for both a sum type and all its cases would lead to code bloat and code duplication. -
If we want to generate derivation infrastructure unconditionally, we are forced to have separate infrastructure for sums and products to avoid code duplication.
Goal
So, the ideal API would be something like Miles' erased API, but with usable types and without needing to create extra classes in cases and enum values. The challenge is to come up with something along these lines.