mark_dj
mark_dj

Reputation: 994

Implicits and blackbox macros

I have the following code;

def $q[U](selector: T => U, value: U)(implicit writer: BSONDocumentWriter[U]): BSONDocument = macro MacrosImpl.getBsonExpr[T, U]

The code of getBsonExpr is;

def getBsonExpr[T, U](c: Context)(selector: c.Expr[T => Any], value: c.Expr[U])(implicit writer: BSONDocumentWriter[U]): c.Expr[BSONDocument] = {
    import c.universe._
    val helper = new Helper[c.type, T](c, selector)

    reify {
      val p = helper.pathStringExpr().splice
      val v = value.splice
      BSONDocument(p -> writer.write(v))
    }
  }

However you can't pass implicits to a macro, so when I compile I get:

macro implementations cannot have implicit parameters other than WeakTypeTag evidences

Is there a way to work around this?

Upvotes: 2

Views: 222

Answers (2)

Leo
Leo

Reputation: 767

You should be using c.inferImplicitValue. A macro does not need to be passed implicit parameters, as it can directly look them up.

You have a problem on your helper, as you can't use params known at runtime during compile time. Fixing this depends on your intent.

Last tip. Use quasiquotes, as they are much more flexible than reify.

I would do something along these lines (not tested).

class A[T] {

def $q[U](selector: T => U, value: U): BSONDocument =
  macro MacrosImpl.getBsonExpr[T, U]

}

object MacrosImpl {

  def getBsonExpr[T, U: c.WeakTypeTag](c: Context)(selector: c.Expr[T => Any], value: c.Expr[U]): c.Expr[BSONDocument] = {
    import c.universe._

    // cannot work as selector is not known at compile time (only its tree his)
    // and macro code is evaluated at compile time
    val helper = new Helper[c.type, T](c, selector)

    // Look for implicit BSONDocumentWrite
    val writer = c.inferImplicitValue(appliedType(typeOf[BSONDocumentWriter[_]], List(weakTypeOf[U])))

    // In case no implicit
    if(writer = EmptyTree) sys.error("No implicit BSONDocumentWriter!")

    val tree = q"""
      val p = ${helper.pathStringExpr()}
      val v = $value
      BSONDocument(p -> $writer.write(v)
    """

    c.Expr[BSONDocument](tree)

  }

}

Upvotes: 0

Martin Ring
Martin Ring

Reputation: 5426

You can use

def $q[U](selector: T => U, value: U)(implicit writer: BSONDocumentWriter[U]): BSONDocument = macro MacrosImpl.getBsonExpr[T, U]

with

def getBsonExpr[T, U](c: Context)
  (selector: c.Expr[T => Any], value: c.Expr[U])
  (writer: c.Expr[BSONDocumentWriter[U]]): c.Expr[BSONDocument] = {
  <...>
}

You can of course not pass the instance of BSONDocumentWriter itself (only the expression tree) to the macro because, well it is a macro and does get compiled before your program.

I don't really know what your macro should do so I can't help you with the implementation. If you provide me with more details I can help you there.

Upvotes: 2

Related Questions