【问题标题】:Scala implicit implicitScala 隐式 隐式
【发布时间】: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


【解决方案1】:

如果你想让扩展方法在隐式转换后仍然适用,你可以修复隐式类的定义

implicit class Foo[A](value: A)(implicit ev: A => Double) {
  def twice: Double = 2 * value
}

implicit def strToDouble(x: String): Double = ???

2.0.twice   //compiles
"2.0".twice //compiles

我知道编译器,twice 是为 Doubles 定义的,而不是 Strings。但是我 确实告诉过你如何从Strings 到Doubles,并且没有 这里模棱两可(据我所知),所以你不应该 注意"2.0".twice可以通过strToDouble("2.0").twice来完成?

根据规范,隐式转换仅适用于三种情况

Why can't the compiler select the correct String.contains method when using this lambda shorthand?

https://scala-lang.org/files/archive/spec/2.13/07-implicits.html#views

2.0.twiceFoo(2.0).twice 的转换是第二种情况,"2.0"strToDouble("2.0") 的转换是第一种情况。如您所见,没有可以将它们一起应用的项目。因此,如果您希望它们一起适用,您应该像我上面显示的那样明确指定。

同样,如果您定义了从AB 和从BC 的转换,这并不意味着您有从AC 的转换

case class A(i: Int)
case class B(i: Int)
case class C(i: Int)
implicit def aToB(a: A): B = B(a.i)
implicit def bToC(b: B): C = C(b.i)

A(1): B    // compiles
B(1): C    // compiles
// A(1): C //doesn't compile

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-01-23
    • 1970-01-01
    • 2014-11-02
    • 2018-09-23
    • 2019-10-02
    • 1970-01-01
    相关资源
    最近更新 更多