【发布时间】:2020-12-08 21:01:52
【问题描述】:
我正在尝试在我的代码中做一些隐含的魔法,但问题非常简单,我已将其提取在这里。这似乎有点奇怪,因为从我所阅读的内容来看,以下内容应该可以工作。
implicit class Foo(value: Double) {
def twice = 2*value
}
2.0.twice
implicit def strToDouble(x: String) = Try(x.toDouble) match {
case Success(d) => d
case Failure(_) => 0.0
}
strToDouble("2.0").twice
val a: Double = "2.0"
val b: Double = "equals 0.0"
"2.0".twice
我得到一个编译错误
value twice is not a member of String
[error] "2.0".twice
我给你编译器,为Doubles 定义了两次,而不是Strings。但我确实告诉过你如何从Strings 到Doubles,这里没有歧义(据我所知),所以你不应该注意到"2.0".twice 可以做到吗通过strToDouble("2.0").twice?
我在这里遗漏了什么吗?或者这是一种优化,以便编译器不会尝试implicits 的所有可能排列(我认为它会以超指数方式增长)。我想我真的在寻找对此的确认或拒绝。
谢谢
【问题讨论】:
-
首先,隐式转换是不好的;更喜欢扩展方法。 - 其次,没有显式返回类型的隐式转换更加危险。 - 第三,那个隐式类也应该是一个值类,所以扩展方法
implicit class Foo(private val value: Double) extends AnyVal没有开销。 - 最后但同样重要的是,编译器永远不会应用多个隐式转换。否则,它会非常缓慢和复杂;而是在String上写一个额外的扩展方法twice。 -
我相信你会在这里找到大部分答案:docs.scala-lang.org/tutorials/FAQ/chaining-implicits.html
-
我认为这是一种保障,而不是一种优化。考虑到所有内容都可以转换为字符串,这也意味着在字符串上定义的任何扩展方法都可以在任何内容之上工作。我还要说,潜在的无休止的隐式转换很容易冻结编译器,同时对正在发生的事情给出很少的提示。
-
@LuisMiguelMejíaSuárez 首先:是的,我同意。但是,我尝试使用的库需要
Seq[T]的隐式,我必须为Seq[Seq[T]]、Seq[Seq[Seq[...]]等的每个实现创建一个隐式,所以我希望定义一个flatMap隐式这会将Seq[Seq[T]]减少到Seq[T],然后这将推广到所有深度。第二:我没有在这里声明返回类型,因为它不是问题的核心,但我同意你的观点。最后:这似乎证实了我的观点,这是一个优化 -
@niurepu 为什么库会要求您添加隐式转换?另外,我认为将
Seq[Seq[T]]与Seq[Seq[Seq[T]]进行不同的思考是有意义的,嵌套的Seqs 操作越复杂,试图隐藏它会隐藏很多复杂性很容易让人们设计出非常糟糕的抽象(无论如何Seqs都是不好的,请改用具体集合)。 - 最后,这并不是真正的优化,而是决定语言语义的设计决策……
标签: scala compiler-errors implicit