Search code examples
scalascala-macros

How to generate top-level class/object with scala macro


As we know, it is easy to create an inner class in some methods with scala macro. But I'd like to know is it possible to generate a top level class/object? If the answer is yes, then how to avoid generate the same class twice? my scala version is 2.11


Solution

    1. Top-level expansions must retain the number of annottees, their flavors and their names, with the only exception that a class might expand into a same-named class plus a same-named module, in which case they automatically become companions as per previous rule.

    https://docs.scala-lang.org/overviews/macros/annotations.html

    So you can transform top-level

    @annot
    class A
    

    into

    class A
    object A
    

    or

    @annot
    object A
    

    into

    class A
    object A
    

    Also there existed c.introduceTopLevel but it was removed.

    1. Context.introduceTopLevel. The Context.introduceTopLevel API, which used to be available in early milestone builds of Scala 2.11.0 as a stepping stone towards type macros, was removed from the final release, because type macros were rejected for including in Scala and discontinued in macro paradise.

    https://docs.scala-lang.org/overviews/macros/changelog211.html

    Scala Macro: Define Top Level Object

    introduceTopLevel has provided a long-requested functionality of generating definitions that can be used outside macro expansions. However, metaprogrammers have quickly discovered that introduceTopLevel is dangerous. Top-level scope is a resource shared between the typechecker and user metaprograms, so mutating it with introduceTopLevel can lead to compilation order problems. For example, if one file in a compilation run relies on definitions created by a macro expansion performed in another file, compiling the former before the latter may lead to unexpected compilation errors.

    https://infoscience.epfl.ch/record/226166/files/EPFL_TH7159.pdf (section 5.2.3 Conclusion)

    If the companion you want to generate already exists then the companion you return in macro annotation's macroTransform will replace the original. You don't need to beware that there will be two "companions", compiler will watch that. But surely normally you match if that's the case (whether there is only annottee or annottee + companion).