Dynamic 类型如何工作以及如何使用它?

2023-11-29

我听说与Dynamic以某种方式可以在 Scala 中进行动态类型。但我无法想象它会是什么样子或者它是如何工作的。

我发现一个人可以继承特质Dynamic

class DynImpl extends Dynamic

The API说可以这样使用它:

foo.method("blah") ~~> foo.applyDynamic("method")("blah")

但当我尝试时,它不起作用:

scala> (new DynImpl).method("blah")
<console>:17: error: value applyDynamic is not a member of DynImpl
error after rewriting to new DynImpl().<applyDynamic: error>("method")
possible cause: maybe a wrong Dynamic method signature?
              (new DynImpl).method("blah")
               ^

这是完全合乎逻辑的,因为在查看之后sources,原来这个特质完全是空的。没有方法applyDynamic定义了,我无法想象如何自己实现它。

有人可以告诉我需要做什么才能让它发挥作用吗?


斯卡拉类型Dynamic允许您调用不存在的对象上的方法,或者换句话说,它是动态语言中“方法缺失”的复制品。

它是正确的,scala.Dynamic没有任何成员,它只是一个标记接口 - 具体实现由编译器填充。至于斯卡拉字符串插值具有描述生成的实现的明确定义的规则。事实上,可以实现四种不同的方法:

  • selectDynamic- 允许编写字段访问器:foo.bar
  • updateDynamic- 允许写入字段更新:foo.bar = 0
  • applyDynamic- 允许使用参数调用方法:foo.bar(0)
  • applyDynamicNamed- 允许使用命名参数调用方法:foo.bar(f = 0)

要使用这些方法之一,编写一个扩展的类就足够了Dynamic并在那里实现方法:

class DynImpl extends Dynamic {
  // method implementations here
}

此外还需要添加一个

import scala.language.dynamics

或者设置编译器选项-language:dynamics因为该功能默认是隐藏的。

选择动态

selectDynamic是最容易实施的一种。编译器将调用翻译为foo.bar to foo.selectDynamic("bar"),因此要求该方法有一个参数列表,期望String:

class DynImpl extends Dynamic {
  def selectDynamic(name: String) = name
}

scala> val d = new DynImpl
d: DynImpl = DynImpl@6040af64

scala> d.foo
res37: String = foo

scala> d.bar
res38: String = bar

scala> d.selectDynamic("foo")
res54: String = foo

正如我们所看到的,也可以显式调用动态方法。

更新动态

Because updateDynamic用于更新该方法需要返回的值Unit。此外,要更新的字段的名称及其值由编译器传递到不同的参数列表:

class DynImpl extends Dynamic {

  var map = Map.empty[String, Any]

  def selectDynamic(name: String) =
    map get name getOrElse sys.error("method not found")

  def updateDynamic(name: String)(value: Any) {
    map += name -> value
  }
}

scala> val d = new DynImpl
d: DynImpl = DynImpl@7711a38f

scala> d.foo
java.lang.RuntimeException: method not found

scala> d.foo = 10
d.foo: Any = 10

scala> d.foo
res56: Any = 10

代码按预期工作 - 可以在运行时向代码添加方法。另一方面,代码不再是类型安全的,如果调用的方法不存在,则也必须在运行时处理。此外,此代码不像动态语言中那样有用,因为不可能创建应在运行时调用的方法。这意味着我们不能做类似的事情

val name = "foo"
d.$name

where d.$name将转变为d.foo在运行时。但这并没有那么糟糕,因为即使在动态语言中,这也是一个危险的功能。

这里要注意的另一件事是updateDynamic需要与selectDynamic。如果我们不这样做,我们会得到一个编译错误 - 这个规则类似于 Setter 的实现,只有在存在同名的 Getter 时才有效。

应用动态

使用参数调用方法的能力由applyDynamic:

class DynImpl extends Dynamic {
  def applyDynamic(name: String)(args: Any*) =
    s"method '$name' called with arguments ${args.mkString("'", "', '", "'")}"
}

scala> val d = new DynImpl
d: DynImpl = DynImpl@766bd19d

scala> d.ints(1, 2, 3)
res68: String = method 'ints' called with arguments '1', '2', '3'

scala> d.foo()
res69: String = method 'foo' called with arguments ''

scala> d.foo
<console>:19: error: value selectDynamic is not a member of DynImpl

方法的名称及其参数再次分为不同的参数列表。如果我们愿意的话,我们可以使用任意数量的参数调用任意方法,但是如果我们想调用一个没有任何括号的方法,我们需要实现selectDynamic.

提示:也可以将 apply-syntax 与applyDynamic:

scala> d(5)
res1: String = method 'apply' called with arguments '5'

应用动态命名

最后一个可用的方法允许我们根据需要命名我们的参数:

class DynImpl extends Dynamic {

  def applyDynamicNamed(name: String)(args: (String, Any)*) =
    s"method '$name' called with arguments ${args.mkString("'", "', '", "'")}"
}

scala> val d = new DynImpl
d: DynImpl = DynImpl@123810d1

scala> d.ints(i1 = 1, i2 = 2, 3)
res73: String = method 'ints' called with arguments '(i1,1)', '(i2,2)', '(,3)'

方法签名的区别在于applyDynamicNamed需要以下形式的元组(String, A) where A是任意类型。


上述所有方法的共同点是它们的参数都可以参数化:

class DynImpl extends Dynamic {

  import reflect.runtime.universe._

  def applyDynamic[A : TypeTag](name: String)(args: A*): A = name match {
    case "sum" if typeOf[A] =:= typeOf[Int] =>
      args.asInstanceOf[Seq[Int]].sum.asInstanceOf[A]
    case "concat" if typeOf[A] =:= typeOf[String] =>
      args.mkString.asInstanceOf[A]
  }
}

scala> val d = new DynImpl
d: DynImpl = DynImpl@5d98e533

scala> d.sum(1, 2, 3)
res0: Int = 6

scala> d.concat("a", "b", "c")
res1: String = abc

幸运的是,还可以添加隐式参数 - 如果我们添加一个TypeTag上下文绑定我们可以轻松检查参数的类型。最好的事情是,即使返回类型也是正确的 - 尽管我们必须添加一些强制转换。

但如果无法找到解决此类缺陷的方法,那么 Scala 就不再是 Scala。在我们的例子中,我们可以使用类型类来避免强制转换:

object DynTypes {
  sealed abstract class DynType[A] {
    def exec(as: A*): A
  }

  implicit object SumType extends DynType[Int] {
    def exec(as: Int*): Int = as.sum
  }

  implicit object ConcatType extends DynType[String] {
    def exec(as: String*): String = as.mkString
  }
}

class DynImpl extends Dynamic {

  import reflect.runtime.universe._
  import DynTypes._

  def applyDynamic[A : TypeTag : DynType](name: String)(args: A*): A = name match {
    case "sum" if typeOf[A] =:= typeOf[Int] =>
      implicitly[DynType[A]].exec(args: _*)
    case "concat" if typeOf[A] =:= typeOf[String] =>
      implicitly[DynType[A]].exec(args: _*)
  }

}

虽然实现看起来不太好,但它的功能是不容质疑的:

scala> val d = new DynImpl
d: DynImpl = DynImpl@24a519a2

scala> d.sum(1, 2, 3)
res89: Int = 6

scala> d.concat("a", "b", "c")
res90: String = abc

最重要的是,还可以组合Dynamic使用宏:

class DynImpl extends Dynamic {
  import language.experimental.macros

  def applyDynamic[A](name: String)(args: A*): A = macro DynImpl.applyDynamic[A]
}
object DynImpl {
  import reflect.macros.Context
  import DynTypes._

  def applyDynamic[A : c.WeakTypeTag](c: Context)(name: c.Expr[String])(args: c.Expr[A]*) = {
    import c.universe._

    val Literal(Constant(defName: String)) = name.tree

    val res = defName match {
      case "sum" if weakTypeOf[A] =:= weakTypeOf[Int] =>
        val seq = args map(_.tree) map { case Literal(Constant(c: Int)) => c }
        implicitly[DynType[Int]].exec(seq: _*)
      case "concat" if weakTypeOf[A] =:= weakTypeOf[String] =>
        val seq = args map(_.tree) map { case Literal(Constant(c: String)) => c }
        implicitly[DynType[String]].exec(seq: _*)
      case _ =>
        val seq = args map(_.tree) map { case Literal(Constant(c)) => c }
        c.abort(c.enclosingPosition, s"method '$defName' with args ${seq.mkString("'", "', '", "'")} doesn't exist")
    }
    c.Expr(Literal(Constant(res)))
  }
}

scala> val d = new DynImpl
d: DynImpl = DynImpl@c487600

scala> d.sum(1, 2, 3)
res0: Int = 6

scala> d.concat("a", "b", "c")
res1: String = abc

scala> d.noexist("a", "b", "c")
<console>:11: error: method 'noexist' with args 'a', 'b', 'c' doesn't exist
              d.noexist("a", "b", "c")
                       ^

宏为我们提供了所有编译时间保证,虽然它在上述情况下没有那么有用,但也许它对于某些 Scala DSL 非常有用。

如果您想获得更多信息Dynamic还有更多资源:

  • The 官方SIP提案那介绍了Dynamic进入斯卡拉
  • Scala 中动态类型的实际使用- 关于SO的另一个问题(但非常过时)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Dynamic 类型如何工作以及如何使用它? 的相关文章

随机推荐

  • 带下拉列表的 Laravel 5.2 过滤器

    我想做下拉列表过滤 我有一个网页 显示了一些带有标题和类别的帖子 该页面有一个下拉菜单nav blade php 我从类别表动态生成下拉列表 但是 当我选择下拉菜单中的一项 例如类别名称 时 我希望页面仅显示该类别的帖子 我还创建了类别和帖
  • 简单的 istream_iterator 问题

    我是 C 新手 如果这是一个愚蠢的问题 我很抱歉 我似乎无法弄清楚为什么这不起作用 它复制到第一个向量中 并且似乎跳过第二个复制调用 include
  • 在通过 $sce.trustAsHtml 添加的字符串中绑定 Angular js 中的数据

    我正在为遗留系统实现一个网络界面 因此来自服务器的数据是固定的 该服务器数据指定要向用户显示的各种控件 例如组合框 按钮等 我已经解析了服务器数据并通过 sce trustAsHtml 添加了控件的 HTML 问题是控件没有绑定到模型 如果
  • 如何动态获取文本框中datagridview列的总和

    我想获得 datagridview 列的总和并将其显示在文本框中 每次输入后 总和应动态更改 为此 我使用文本框的 textChanged 事件 但是当输入内容时 它不会显示任何结果 我想在文本框中动态获取结果 我想避免使用求和按钮 下面是
  • 导出android库项目以供重用,无需源代码

    出于安全原因 我需要导出没有源代码的库项目 不幸的是 库项目中生成的 jar 文件不包含资源 我不能指望这个图书馆的用户能够处理图书馆所需的任何资源 已经有类似的帖子了 但我还没有看到解决方案 以下食谱曾经有效 尽管我最近没有尝试过 步骤
  • Java 进程 - 无法解压缩 zip 文件

    我正在尝试解压缩一些 zip 文件 它大约有 65 兆 代码片段如下 这个方法实际上解压一个文件 public synchronized void execute Path zipFile final ProcessBuilder buil
  • 动态创建匿名类型? [复制]

    这个问题在这里已经有答案了 我想创建一个可以动态设置属性名称的匿名类型 它不必是匿名类型 我想要实现的就是动态设置任何对象属性名称 它可以是 ExpandoObject 但字典对我不起作用 您有什么建议 Only ExpandoObject
  • Python 3.2.2 open('C:\file.txt') 不起作用

    诚然 我是一个新手 但是 我认为 我按照教程所说的进行了操作 对于我安装的版本 但我无法让 Python 读取文件 其他人遇到过长文件名或路径的问题 但我让我的文件名或路径简短而甜蜜 所以我想 文件本身在一行中包含一个单词 print st
  • XSLT 中的词频计数器

    我正在尝试用 XSLT 制作一个词频计数器 我希望它使用停用词 我开始了迈克尔 凯的书 但我很难让停用词发挥作用 此代码适用于任何源 XML 文件
  • 为空数据框创建异常

    我正在接受用户输入来获取特定股票的历史数据 我想处理 2 个可能的错误 第一个是由于错误输入而生成的错误 并且代码生成错误 第二个是如果输入错误但代码被执行并且我得到一个空数据框 while True try us input Enter
  • 获取对 std::thread::_M_start_thread 的未定义引用

    我正在构建一个使用我从源代码构建的第三方库 Box2D MT 的应用程序 链接时 我收到此未定义的引用错误 b2Threading cpp text ZNSt6threadC2IM12b2ThreadPoolFviEJPS1 iEEEOT
  • 滚动 PCA 并绘制主成分的比例方差

    我使用以下代码来执行 PCA PCA lt prcomp Ret1 center TRUE scale TRUE summary PCA 我得到以下结果 Importance of components PC1 PC2 PC3 PC4 St
  • 替换函数内的异步加载

    我正在使用 Javascript 进行替换 我做了这样的事情 var replacedText originalText replace regex function value i return value some additional
  • 如何为具有 SRC、OBJ 和 BIN 子目录的 C 项目创建 Makefile?

    几个月前 我想出了以下通用的Makefile对于学校作业 Generic Makefile Author email protected Date 2010 11 05 Changelog 0 01 first version projec
  • Thymeleaf 嵌套迭代触发 org.thymeleaf.exceptions.TemplateInputException

    我正在尝试迭代对象列表并生成一个div class card deck every 4对象和嵌套div class card 对于每个对象 这是生成异常的代码line 234 UPDATE Note line 234中提到html并拥有由于
  • 如何在使用出站网关上传文件之前检查 aws s3 存储桶是否可用

    在 Spring Boot 应用程序中使用 aws s3 出站适配器 尝试上传 s3 存储桶中的文件 想在上传文件之前检查存储桶是否可用 如果存储桶不可用 则需要抛出错误 对此提出建议
  • 在 javascript (lodash) 中合并部分重复的数组

    我有一个很大的 javascript 数组 其中包含一些人在不同年份购买的汽车 简化后的数组是这样的 const owners name john hasCar true yearBought 2002 name john hasCar t
  • 具有多个已定义的入口点错误

    我有以下代码 namespace WpfApplication2
  • 从异步方法返回多个结果?

    我目前有一个看起来像这样的方法 typedef void MyCallback int status String body void makeRequest String url MyCallback callback async if
  • Dynamic 类型如何工作以及如何使用它?

    我听说与Dynamic以某种方式可以在 Scala 中进行动态类型 但我无法想象它会是什么样子或者它是如何工作的 我发现一个人可以继承特质Dynamic class DynImpl extends Dynamic The API说可以这样使