title: Howto model named parameters in method invocations with Scala macros?
body: <p>There are use cases where it is useful to create a copy of an object which is an instance of a case class of a set of case classes, which have a specific value in common.</p>
  
  <p>For example let's consider the following case classes:</p>
  
  <pre><code>case class Foo(id: Option[Int])
  case class Bar(arg0: String, id: Option[Int])
  case class Baz(arg0: Int, id: Option[Int], arg2: String)
  </code></pre>
  
  <p>Then <code>copy</code> can be called on each of these case class instances:</p>
  
  <pre><code>val newId = Some(1)
  
  Foo(None).copy(id = newId)
  Bar("bar", None).copy(id = newId)
  Baz(42, None, "baz").copy(id = newId)
  </code></pre>
  
  <p>As described <a href="http://www.scala-lang.org/node/4620" rel="nofollow noreferrer">here</a> and <a href="https://stackoverflow.com/questions/12370244/case-class-copy-method-with-superclass">here</a> there is no simple way to abstract this like this:</p>
  
  <pre><code>type Copyable[T] = { def copy(id: Option[Int]): T }
  
  // THIS DOES *NOT* WORK FOR CASE CLASSES
  def withId[T &lt;: Copyable[T]](obj: T, newId: Option[Int]): T =
    obj.copy(id = newId)
  </code></pre>
  
  <p>So I created a scala macro, which does this job (almost):</p>
  
  <pre><code>import scala.reflect.macros.Context
  
  object Entity {
  
    import scala.language.experimental.macros
    import scala.reflect.macros.Context
  
    def withId[T](entity: T, id: Option[Int]): T = macro withIdImpl[T]
  
    def withIdImpl[T: c.WeakTypeTag](c: Context)(entity: c.Expr[T], id: c.Expr[Option[Int]]): c.Expr[T] = {
  
      import c.universe._
  
      val currentType = entity.actualType
  
      // reflection helpers
      def equals(that: Name, name: String) = that.encoded == name || that.decoded == name
      def hasName(name: String)(implicit method: MethodSymbol) = equals(method.name, name)
      def hasReturnType(`type`: Type)(implicit method: MethodSymbol) = method.typeSignature match {
        case MethodType(_, returnType) =&gt; `type` == returnType
      }
      def hasParameter(name: String, `type`: Type)(implicit method: MethodSymbol) = method.typeSignature match {
        case MethodType(params, _) =&gt; params.exists { param =&gt;
          equals(param.name, name) &amp;&amp; param.typeSignature == `type`
        }
      }
  
      // finding method entity.copy(id: Option[Int])
      currentType.members.find { symbol =&gt;
        symbol.isMethod &amp;&amp; {
          implicit val method = symbol.asMethod
          hasName("copy") &amp;&amp; hasReturnType(currentType) &amp;&amp; hasParameter("id", typeOf[Option[Int]])
        }
      } match {
        case Some(symbol) =&gt; {
          val method = symbol.asMethod
          val param = reify((
            c.Expr[String](Literal(Constant("id"))).splice,
            id.splice)).tree
          c.Expr(
            Apply(
              Select(
                reify(entity.splice).tree,
                newTermName("copy")),
              List( /*id.tree*/ )))
        }
        case None =&gt; c.abort(c.enclosingPosition, currentType + " needs method 'copy(..., id: Option[Int], ...): " + currentType + "'")
      }
  
    }
  
  }
  </code></pre>
  
  <p>The last argument of <code>Apply</code> (see bottom of above code block) is a List of parameters (here: parameters of method 'copy'). How can the given <code>id</code> of type <code>c.Expr[Option[Int]]</code> be passed as named parameter to the copy method with the help of the new macro API?</p>
  
  <p>In particular the following macro expression</p>
  
  <pre><code>c.Expr(
    Apply(
      Select(
        reify(entity.splice).tree,
        newTermName("copy")),
      List(/*?id?*/)))
  </code></pre>
  
  <p>should result in</p>
  
  <pre><code>entity.copy(id = id)
  </code></pre>
  
  <p>so that the following holds</p>
  
  <pre><code>case class Test(s: String, id: Option[Int] = None)
  
  // has to be compiled by its own
  object Test extends App {
  
    assert( Entity.withId(Test("scala rulz"), Some(1)) == Test("scala rulz", Some(1)))
  
  }
  </code></pre>
  
  <p>The missing part is denoted by the placeholder <code>/*?id?*/</code>.</p>