【问题标题】:Implicit conversions causing infinite recursion, but that shouldn't typecheck导致无限递归的隐式转换,但这不应该进行类型检查
【发布时间】:2015-01-12 16:51:43
【问题描述】:

我正在尝试为将 Squants Time 作为参数的类编写 Specs2 测试。诀窍在于,这两个工具都定义了一个隐式方法,该方法添加了一种称为“秒”的方法来将数字转换为它们自己的表示形式(一种情况下是 squants.time.Seconds,另一种情况下是 org.specs2.time.Duration),并且不幸的是,错误的似乎占了上风。

val a = new MyClass(10 seconds)  // doesn't build because MyClass wants a Time instead of a Duration

需要明确的是,我可以通过不依赖隐式来构造 Squants Time 来解决这个问题,这不是问题。

我更喜欢隐式,所以我决定添加一个隐式来将持续时间转换为时间:

implicit def specsTimeToSquantsTime(t: org.specs2.time.Duration): squants.time.Time = squants.time.Seconds(t.toSeconds)

这让它进行了类型检查,但是当我运行测试时,我遇到了堆栈溢出并且堆栈跟踪没有多大意义,它说我的隐式转换正在调用自身(它不会进行类型检查,即使考虑到上面的代码,它仍然是不可能的!):

[error] package.TestConversions$.specsTimeToSquantsTime(TestConversions.scala:9)
[error] package.TestConversions$.specsTimeToSquantsTime(TestConversions.scala:9)
[error] package.TestConversions$.specsTimeToSquantsTime(TestConversions.scala:9)
...

所以我有三个问题:这里发生了什么?有一个更好的方法吗?我可以手动隐藏 Int=>Duration 隐式吗?

【问题讨论】:

  • 我在specs Duration 中看不到toSeconds 方法,因此在隐式转换中进行了隐式转换。
  • Arg!我瞎了眼这绝对是答案,谢谢! (编辑:我的意思是,如果您将评论转换为答案,我很乐意将其标记为这样)

标签: scala implicit


【解决方案1】:

方法toSecondsTime类中定义:

final class Time private (val value: Double) extends Quantity[Time] {    
  ...
  def toSeconds = to(Seconds)
  ...
}

https://github.com/garyKeorkunian/squants/blob/master/src/main/scala/squants/time/Time.scala

因此,当编译器看到在 Duration 上调用此方法时 - 它正在搜索适当的隐式方法,它是 specsTimeToSquantsTime,它又包含 Duration.toSeconds,它需要从 DurationTime 的隐式转换等等等等。所以你在运行时得到了无限递归调用,这在编译器方面是完全正确的,因为理论上你可以停止这种递归并且没有通用的方法(参见halting problem)来检测它。

您可以在规范中混合使用 NoTimeConversions 特征以避免隐式转换:

此特征可用于停用时间转换(以避免 例如与 Akka 的转换冲突

【讨论】:

    猜你喜欢
    • 2018-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多