See here for the current (draft) proposal. It is still incomplete, but I think it is in a fleshed-out enough state to start the pre-sip discussion.
There are several outstanding questions namely about mirror support which I’m still unclear on:
productElement be promoted onto the
- Should a new anonymous class be created each time at the callsite; does the reasoning which went into including the
TupleMirror apply here?
- Is it a problem to include a class which only compiles with JDK16+ in the compilation process of the runtime library?
- What happens if I refer to a method on the companion object for the Java class at runtime? Do we still go through the same desugar steps in the compiler with a compiled jar?
I have also left the specification until some of the questions are (hopefully) answered by the Pre-SIP discussion.
It should be noted, that the current eco-system implicitly assumes that a
Mirror.Product returns a class which extends
Product even though there are no type bounds on the actual trait itself. This is implied by the example in the official documentation and can be seen in the source code of well-used libraries in the wild.
Releasing the changes proposed would mean that a codec would be able to be derived for a record, but the program would fail at runtime — which is not ideal behaviour.
This is pretty unfortunate indeed. One way to work around this would be to create new types that would generalize the existing Mirror types. This might be worth it if we’d like to support other non-Product things, for example, classes with multiple parameter lists. It could also let us replace fromProduct by something that takes into account
apply methods: Case class deserialization doesn't go through `apply` in Scala 3 · Issue #552 · com-lihaoyi/upickle · GitHub
It’s going to make our build a bit painful but it shouldn’t be a showstopper.
Something equivalent to your desugaring logic will need to be added to dotty/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala at main · lampepfl/dotty · GitHub probably at the same place where we currently call
NamerOps.addConstructorProxies which is another kind of magic inline method.
I like this idea of adding new mirrors,
One other relevant suggestion is here from @lihaoyi. If we introduced a new mirror type which was something like,
// type aliases same as `Mirror.Product`
type MirrorElemLabels <: Tuple
type MirroredLabel <: String
// These allow a user of the mirror to abstract over named products at runtime
def productArity: Int
def productElementAt(i: Int): Any
def fromProduct(p: Product): MirroredMonoType
And make it derivable for both
T <: NamedTuple and records.
Perhaps we could even use it to address @odersky point from below:
But the problem is we can’t abstract over it. If we want to have a generic type that reflects names and types, we are back to
Since you can use the methods on the mirror to abstract over the
T <: NamedProduct.