scala 宏:延迟类型推断

2023-12-25

序言:这是基于@Travis Brown 的基于宏的解决方案 https://stackoverflow.com/a/13447439/185840复制案例类属性。

Given:

trait Entity[E <: Entity[E]]{self:E=>
  def id: Int
  def withId(id: Int) = MacroCopy.withId(self,id)
}
case class User(id: Int, name: String) extends Entity[User]

和宏实现:

object MacroCopy {
  import scala.language.experimental.macros
  import scala.reflect.macros.blackbox.Context
  def withId[T](entity: T, id: Int): T = macro withIdImpl[T]

  def withIdImpl[T: c.WeakTypeTag]
    (c: Context)(entity: c.Expr[T], id: c.Expr[Int]): c.Expr[T] = {
    import c.universe._

    val tree = reify(entity.splice).tree
    val copy = entity.actualType.member(TermName("copy"))

    val params = copy match {
      case s: MethodSymbol if (s.paramLists.nonEmpty) => s.paramLists.head
      case _ => c.abort(c.enclosingPosition, "No eligible copy method!")
    }
    c.Expr[T](Apply(
      Select(tree, copy),
      AssignOrNamedArg(Ident(TermName("id")), reify(id.splice).tree) :: Nil
    ))
  }
}

有没有一种方法可以以某种方式推迟类型推断,使宏在User而不是实体的自我类型?就目前情况而言,类型检查器对User的 case 类复制方法,因为它看到的只是类型的值E.

我想做:

val u = User(2,"foo")
u.withId(3)

我见过的大多数替代解决方案都需要将 withId 定义为实体特征中的抽象,然后在every案例类,如果可能的话,宁愿避免这种情况。


weakTypeOf与实例类上绑定的上下文相结合,提供所需的“延迟”类型推断。

trait Entity[E <: Entity[E]]{self:E=>
  def id: Int
  def withId(id: Int) = MacroCopy.withIdImpl[E]
}
case class User(id: Int, name: String) extends Entity[User]

object MacroCopy {
  import scala.language.experimental.macros
  import scala.reflect.macros.blackbox.Context

  def withIdImpl[T <: Entity[T]: c.WeakTypeTag] // context bound on Entity
    (c: Context)(id: c.Expr[Int]): c.Expr[T] = {
    import c.universe._

    val tree = reify( c.Expr[T](c.prefix.tree).splice ).tree
    val copy = weakTypeOf[T].member(TermName("copy")) // now lookup case class' copy method
    val params = copy match {
      case s: MethodSymbol if (s.paramLists.nonEmpty) => s.paramLists.head
      case _ => c.abort(c.enclosingPosition, "No eligible copy method!")
    }
    c.Expr[T](Apply(
      Select(tree, copy),
      AssignOrNamedArg(Ident(TermName("id")), reify(id.splice).tree) :: Nil
    ))
  }
}

我们现在可以写foo.withId(2)代替之前的尝试,foo.withId(foo, 2),极其简洁。可能想知道为什么不这样做:foo.copy(id = 2)?对于具体情况,效果很好,但是当您需要在更抽象的级别应用它时,它根本不起作用。

The following also does not work, seems we must work with concrete case classes instances, so close ;-( For example, let's say you have a DAO and you want to ensure that all updated entities have a valid id. The above macro allows you to do something like:

def update[T <: Entity[T]](entity: T, id: Int)(implicit ss: Session): Either[String,Unit] = {
  either( byID(id).mutate(_.row = entity.withId(id)), i18n("not updated") )
}

由于 Entity 是一个特征而不是一个案例类,如果没有宏,就没有编译时方法来模拟entity.copy(id = id).

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

scala 宏:延迟类型推断 的相关文章

随机推荐