improvement-proposals
improvement-proposals copied to clipboard
SIP-63 - Scala 3 Macro Annotations
Please open a thread on contributors.scala-lang.org to gain community feedback, and link it to this proposal.
Please open a thread on contributors.scala-lang.org to gain community feedback, and link it to this proposal.
Here is the contributors thread: https://contributors.scala-lang.org/t/scala-3-macro-annotations-sip-63-discussions/6593
One question that came up in the last SIP meeting is whether or not it's possible to implement https://github.com/scala/improvement-proposals/pull/78 using macro annotations, and what it would look like if we did. @nicolasstucki do you have any insights there? We already have full implementations and test cases in the https://github.com/com-lihaoyi/unroll/ repo, so it should be possible to try it out using macro annotations and see if it can be made to work
One question that came up in the last SIP meeting is whether or not it's possible to implement https://github.com/scala/improvement-proposals/pull/78 using macro annotations, and what it would look like if we did. @nicolasstucki do you have any insights there?
I wrote and example implementation of a strip down version of https://github.com/scala/improvement-proposals/pull/78 for unrolling the last parameters of a def. This implementation shows that we have access to all the we need to construct the definition new definition.
However, we still have some limitations in the current implementation, such as missing the parameter macro annotations (we are woking on a prototype and should have something soonish). We might also me missing some helper functions in the reflection API to help us deal with this transformation more elegantly (for example a simple way to get the default arguments).
I have not tried with constructors yet. We will have a similar situation for class constructors. But, case class constructors parameter unrolling might not fit the current specification of macro annotations. As the annotation is on the parameter, the transformed tree will be the constructor of the case class, this only allows us to add methods in the case class itself but not in the compassion object.
@jchyb and @hamzaremmal will take over the development of this SIP.
This proposal was discussed at the last SIP meeting, but there was concern that the current proposal is currently too limited and did not have enough usecases as-is:
- The vote for accepting it lead to a 4/4 split.
- The committee unanimously voted against rejecting the proposal.
In particular, the fact that existing libraries like cask and mainargs cannot be ported to work as-is without requiring extra annotations seems problematic. If we look at the cask example from @lihaoyi again:
// Cask
object MinimalApplication extends cask.MainRoutes{
@cask.get("/")
def hello() = {
"Hello World!"
}
@cask.post("/do-thing")
def doThing(request: cask.Request) = {
request.text().reverse
}
initialize()
}
The current proposal would require an extra annotation on the object definition to gather all the method-level annotations.
To avoid this extra level of boilerplate, I'd like to suggest we introduce "inheritable macro annotation", concretely if we define MainRoutes in cask as:
class caskGen extends InheritableMacroAnnotation { ... }
@caskGen trait MainRoutes { ... }
Then we should desugar:
object MinimalApplication extends cask.MainRoutes
into:
@caskGen object MinimalApplication extends cask.MainRoutes
I believe inheritable macro-annotations could be useful in many situations. For example, if I want to memoize instances of an open class hierarchy, I'd like to be able to define:
class memo extends InheritableMacroAnnotation { ... }
@memo trait Base
so that anyone inheriting from Base automatically gets a memoized class without having to remember to put the annotation on the class.