Mirrors and inline methods present a significant improvement over macros for a great many reflective tasks. Unfortunately they don’t expose annotations on mirrored members.
This means that mirrors by themselves aren’t enough for a great many use-cases in which they would otherwise be a perfect solution; such as various forms of serialisation and persistence where annotations are commonly used to specify alternate names, identify primary keys, control field ordering, flag values as needing encryption, and other such tasks.
If product mirrors were able to expose annotations, this would eliminate one of the larger categories for which macros are still a painful necessity.
Could you provide a self contained example that shows the library, macro and user code. As well as some details on how you intend to access the mirror information.
package my.pkg
case class Foo(
a: Int,
b: Double @deprecated("don't use this", "1.2")
)
That would give us:
ProductMirror {
type MirroredLabel = "Foo"
type MirrorredMonoType = my.pkg.Foo
type MirroredElemLabels = ("a", "b")
type MirroredElemTypes = (Int, Double)
}
Allowing only one annotation per value we could introduce a new AnnotationMirror:
AnnotationMirror {
type MirroredLabel
type MirroredMonoType
type MirroredParamNames <: Tuple
type MirroredParamTypes <: Tuple
type MirroredParamValues <: Tuple
}
and a tuple of these be added as a new type on product mirrors, using Nothing or some equivalent to show the absence of an attribute:
ProductMirror {
type MirroredLabel = "Foo"
type MirrorredMonoType = my.pkg.Foo
type MirroredElemLabels = ("a", "b")
type MirroredElemTypes = (Int, Double)
type MirroredElemAttributes = (
Nothing,
AnnotationMirror {
type MirroredMonoType = scala.deprecated
type MirroredLabel = "deprecated"
type MirroredParamNames = ("message", "since")
type MirroredParamTypes = (String, String)
type MirroredParamValues = ("don't use this", "1.2")
}
)
}
Access would be via the same techniques we already use to work with mirrors
The MirroredParamValues looks suspicious. Mirrors do not give constructor arguments in general. This addition would make mirrors escape the safety of types.
Another concern is that there would not be an easy way to know which constructor of that annotation is used. Consider overloaded constructors and default parameters, this is already enough to complicate this beyond what is paractical/feasable.
@kevinwrighthere is the example of macro which is reading annotations on the class and the fields as well. I don’t know how this things looked in Scala 2, but in Scala 3 I wouldn’t say that it’s complicated or painful.
A base class for static annotations. These are available to the Scala type checker or Scala reflection, even across different compilation units.
The truth is that static annotations require parameters to be literals and we are already dealing with something available at type level, so there’s no safety challenge here.
Any progress here (or elsewhere)? Being able to see annotations of mirrored type and its fields (even if ConstantAnnotation only) would be great. Writing code generators using mirrors is still easier than using quotes.
Or is there other recommended mechanism how to access annotation during code generation?