Search code examples
scalamacrosscala-macrosscala-quasiquotesreify

Can Scala macros be defined inside a class (as methods of that class)?


I need Scala macros (reify, quasiquote, macro impl) for my Scala assertions library.


I want to be able to do this:

object1.assertEquals(object2) // success: object1 = object2

Or like this:

3.assertEquals(1 + 1) // failure: 1 + 1 /= 3 

Can Scala macros be defined inside an implicit class?


Solution

  • The implementation has to be in an object or a macro bundle, but the method that is implemented by the macro can be in an implicit class. Notice val self = q"${c.prefix}.self" that is needed to get the reference to the object that is wrapped by the implicit class.

    import scala.language.experimental.macros
    import scala.reflect.macros.blackbox.Context
    
    
    object assertions {
      implicit class AssertEquals[T](val self: T) extends AnyVal {
        def assertEquals(other: T): Unit = macro AssertionMacros.assertEquals[T]
      }
    
      object AssertionMacros {
        def assertEquals[T](c: Context)(other: c.Tree): c.Tree = {
          import c.universe._
          val self = q"${c.prefix}.self"
          q"_root_.scala.Predef.assert($self == $other)"
        }
      }
    }
    

    Usage:

    scala> import assertions._
    import assertions._
    
    scala> "foo" assertEquals "foo"
    
    scala> "foo" assertEquals "bar"
    java.lang.AssertionError: assertion failed
      at scala.Predef$.assert(Predef.scala:151)
      ... 43 elided