Скала рекурсивный макрос?
Мне было интересно, поддерживает ли 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
, Как я могу это сделать?
Как я могу написать эти строки в общем виде?
1 ответ
На самом деле мне удалось заставить его работать: 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 применяет один и тот же макрос рекурсивно.