Scala递归宏?

6
我想知道Scala是否支持递归宏扩展,例如我正在尝试编写一个镜头库,其中包含一个执行以下操作的宏:
case class C(d: Int)
case class B(c: C)
case class A(b: B)

val a = A(B(C(10))
val aa = lens(a)(_.b.c.d)(_ + 12)
assert(aa.b.c.d == 22)

给定 lens(a)(_.b.c.d)(f),我希望将其转换为 a.copy(b = lens(a.b)(_.c.d)(f))
编辑: 在这里我取得了一些不错的进展 然而,我无法想出一种通用方法来创建一个访问器 List[TermName],例如对于上面的例子,假设我有 List(TermName('b'), TermName('c'), TermName('d')),我想生成一个匿名函数 _.b.c.d ,即 (x: A) => x.b.c.d。 我该怎么做?

基本上,我如何以通用的方式编写这些行


你的库看起来非常类似于这个库。你可以从那里借鉴一些想法。 - Vladimir Matveev
1个回答

3

实际上,我设法让它工作起来了:https://github.com/pathikrit/sauron/blob/master/src/main/scala/com/github/pathikrit/sauron/package.scala

这是完整的源代码:

package com.github.pathikrit

import scala.reflect.macros.blackbox

package object sauron {

  def lens[A, B](obj: A)(path: A => B)(modifier: B => B): A = macro lensImpl[A, B]

  def lensImpl[A, B](c: blackbox.Context)(obj: c.Expr[A])(path: c.Expr[A => B])(modifier: c.Expr[B => B]): c.Tree = {
    import c.universe._

    def split(accessor: c.Tree): List[c.TermName] = accessor match {    // (_.p.q.r) -> List(p, q, r)
      case q"$pq.$r" => split(pq) :+ r
      case _: Ident => Nil
      case _ => c.abort(c.enclosingPosition, s"Unsupported path element: $accessor")
    }

    def join(pathTerms: List[TermName]): c.Tree = (q"(x => x)" /: pathTerms) {    // List(p, q, r) -> (_.p.q.r)
      case (q"($arg) => $pq", r) => q"($arg) => $pq.$r"
    }

    path.tree match {
      case q"($_) => $accessor" => split(accessor) match {
        case p :: ps => q"$obj.copy($p = lens($obj.$p)(${join(ps)})($modifier))"  // lens(a)(_.b.c)(f) = a.copy(b = lens(a.b)(_.c)(f))
        case Nil => q"$modifier($obj)"                                            // lens(x)(_)(f) = f(x)
      }
      case _ => c.abort(c.enclosingPosition, s"Path must have shape: _.a.b.c.(...), got: ${path.tree}")
    }
  }
}

是的,Scala会递归应用相同的宏。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接