gpt4 book ai didi

斯卡拉宏 : defer type inference

转载 作者:行者123 更新时间:2023-12-01 06:28:17 25 4
gpt4 key购买 nike

序言:这是基于@Travis Brown 的 macro based solution复制案例类属性。

鉴于:

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 一无所知。的案例类复制方法,因为它看到的只是 E 类型的值.

我想做:
val u = User(2,"foo")
u.withId(3)

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

最佳答案

weakTypeOf结合实例类上的上下文绑定(bind)提供了所需的“延迟”类型推断。

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) ?对于可以正常工作的具体案例,但是当您需要在更抽象的级别上应用它时,它根本不起作用。

以下也不起作用,似乎我们必须使用具体的案例类实例,如此接近;-(例如,假设您有一个 DAO,并且您想确保所有更新的实体都有一个有效的 id。上面的宏允许您做类似的事情:
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) 的方法。 .作为一种解决方法,我重新定义了 DAO 更新方法,如下所示:
def update[T <: Entity[T]](fn: id => T, id: Int)(implicit ss: Session): Either[String,Unit] = {
either( byID(id).mutate(_.row = fn(id)), i18n("not updated") )
}

这至少迫使一个人向更新方法提供 u.withId(_:Int) 函数。比在运行时拥有可能无效的实体要好,但仍然不如在重要之前执行 withId 那样优雅(即持久化到 DB),从而避免传递具体函数实例以更新的骡子工作(样板),叹息,必须有一个用宏解决这个问题的方法。

在其他新闻中,在编写了我的第一个宏之后,我真的很喜欢这里的潜力,很棒的东西;-)

关于斯卡拉宏 : defer type inference,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25752645/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com