scala - 以编程方式将返回特定类型的函数实现包装到另一个函数中
问题描述
我想将返回某个类型的 scala 项目中的所有用户定义函数包装T
到一个接受 aT
和函数名作为参数的函数中。
例如。
鉴于此功能在范围内:
def withMetrics[T](functionName: String)(f: => Try[T]): Try[T] = {
f match {
case _: Success[T] => println(s"send metric: success for $functionName")
case _: Failure[T] => println(s"send metric: failure for $functionName")
}
f
}
用户可以为他们的函数发送指标,这些指标Try
通过执行返回
def userDefinedFunction: Try[_] =
withMetrics("userDefinedFunction"){
somethingRisky: Try[_]
}
但我希望用户只需要定义
def userDefinedFunction: Try[_] =
somethingRisky: Try[_]
并让他的业务逻辑Try
隐含地返回withMetrics
。
请注意,用户不应该对代码进行注释,因为这可能会导致他忘记它。相反,在他的项目中定义的所有用户函数都应该withMetrics
自动包装进去。
如何通过使用 Scala 2 或 dotty 宏来实现这一点?或者这可以通过其他方式实现吗?
解决方案
您可以创建宏注解并用它注解您想要检测方法的所有类、对象和特征。
import scala.annotation.{StaticAnnotation, compileTimeOnly}
import scala.language.experimental.macros
import scala.reflect.macros.whitebox
object Macros {
@compileTimeOnly("enable macro paradise (or -Ymacro-annotations in 2.13) to expand macro annotations")
class withMetrics extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro WithMetricsMacro.impl
}
object WithMetricsMacro {
def impl(c: whitebox.Context)(annottees: c.Tree*): c.Tree = {
import c.universe._
def modify(stats: Seq[Tree]): Seq[Tree] = stats.map {
case q"$mods def $tname[..$tparams](...$paramss): $tpt = $expr" =>
q"$mods def $tname[..$tparams](...$paramss): $tpt = withMetrics(${tname.toString}){ $expr }"
}
annottees match {
case q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }" :: tail =>
q"""
$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..${modify(stats)} }
..$tail
"""
case q"$mods trait $tpname[..$tparams] extends { ..$earlydefns } with ..$parents { $self => ..$stats }" :: tail =>
q"""
$mods trait $tpname[..$tparams] extends { ..$earlydefns } with ..$parents { $self => ..${modify(stats)} }
..$tail
"""
case q"$mods object $tname extends { ..$earlydefns } with ..$parents { $self => ..$stats }" :: Nil =>
q"$mods object $tname extends { ..$earlydefns } with ..$parents { $self => ..${modify(stats)} }"
case _ =>
c.abort(c.enclosingPosition, "Not a class, object or trait ")
}
}
}
}
import Macros._
import scala.util.{Failure, Success, Try}
object App {
@withMetrics
class A {
def userDefinedFunction: Try[String] = Try("aaa")
}
def withMetrics[T](functionName: String)(f: => Try[T]): Try[T] = {
f match {
case _: Success[T] => println(s"send metric: success for $functionName")
case _: Failure[T] => println(s"send metric: failure for $functionName")
}
f
}
def main(args: Array[String]): Unit = {
(new A).userDefinedFunction // send metric: success for userDefinedFunction
}
}
这不会修改内部类、对象、特征中的嵌套方法和方法。如有必要,也可以使用scala.reflect.api.Trees.Traverser/Transformer
. 或者,您可以在必要时对内部类、对象、特征进行注释。
推荐阅读
- c# - 从 void 方法检查 IsCompleted,无法返回
- python - TensorFlow 在其较新版本中如何将其内部列表视为张量是否有任何变化?
- vue.js - 修改注册按钮的大小
- javascript - Angular:在新窗口中打开组件,无法更改 URL 和 html 头与主应用程序 html 中的相同
- bash - 无法使用 conda run 运行交互式 shell 脚本
- reactjs - 如何加载图像反应 babel
- c# - 靠墙移动时如何防止从墙上反弹?
- javascript - Primefaces p:selectBooleanCheckBox 不显示复选标记
- python - UndefinedUnitError 如何处理?
- javascript - 选中特定类分组 div 的复选框