Search code examples
scalascala-macros

Scala macros invoke a compile-time determined function name


I have the following code:

def invokeFunction(logger: Logger, fName: String) = macro invokeFunctionImpl

//...

def invokeFunctionImpl(c: Context)(logger: c.Expr[Logger], fName: c.Expr[String]) ={
  import c.universe._
  // i'd like to invoke: logger.${fName}("some message")
}

Another problem I'm facing is naming a function during compile-time, using a given argument:

def createFunction(fName: String) = macro createFunctionImpl

//...

def createFunctionImpl(c: Context)(fName: c.Expr[String], param: c.Expr[String]) ={
  import c.universe._
  // i'd like to create: def functionNamed${fName}(param: [paramType]) = {
    // implementation unimportant
}

Any help would be appreciated.


Solution

  • invokeFunction should be like

    def invokeFunction(logger: Logger, fName: String): Unit = macro invokeFunctionImpl
    
    def invokeFunctionImpl(c: blackbox.Context)(logger: c.Expr[Logger], fName: c.Expr[String]): c.Expr[Unit] ={
      import c.universe._
      val q"${fNameStr: String}" = fName.tree
      c.Expr(q"""$logger.${TermName(fNameStr)}("some message")""")
    }
    

    I added return type. I assume that fName is a compile-time String literal (otherwise you'll have MatchError).

    Regarding createFunction try

    def createFunction(fName: String, param: String): Unit = macro createFunctionImpl
    
    def createFunctionImpl(c: blackbox.Context)(fName: c.Expr[String], param: c.Expr[String]): c.Expr[Unit] ={
      import c.universe._
      val q"${fNameStr: String}" = fName.tree
      val q"${paramNameStr: String}" = param.tree
    
      c.Expr(q"""
        def ${TermName(fNameStr)}(${TermName(paramNameStr)}: ParamType) = ???
    
        ()
      """)
    }
    

    Please notice that signatures of macro and its implementation must correspond to each other. So I added param to createFunction. And I added return type. Also it's important that a function being created will be accessible only inside the block that our macro is expanded to. New definitions is not what def macros are normally intended for (macro annotations are).

    I'm not sure I understand completely what you're doing. It will be easier to answer if you provide code how you're going to use your macros.