简体   繁体   English

scala如何确定TreeSet构造函数的隐式类型参数

[英]How scala determines implicit type parameters for TreeSet constructor

I'm doing excersises from 'Scala for the impatient' book and stumbled across this magic. 我正在为“不耐烦的”书中的“斯卡拉”做点什么,偶然发现了这个魔法。 I have a simple scala object for executing code: 我有一个简单的scala对象来执行代码:

object Main {

  def main(args: Array[String]): Unit = {
    println(charIndex("Missisipi"))
    //var s = scala.collection.immutable.SortedSet[Int]()
  }

  def charIndex(s: String) : scala.collection.mutable.Map[Char, scala.collection.mutable.TreeSet[Int]] = {
    val m = scala.collection.mutable.Map[Char, scala.collection.mutable.TreeSet[Int]]()
    s.zipWithIndex.foreach{
      case (c, i) => m += c -> (m.getOrElse(c, scala.collection.mutable.TreeSet()) += i)
    }
    m
  }
}

No matter how it looks like (I did it the ugliest way), but in this case it doesn't compile with error: 无论它看起来如何(我用最丑陋的方式),但在这种情况下,它不会编译错误:

Error:(14, 80) diverging implicit expansion for type scala.math.Ordering[T1] starting with method Tuple9 in object Ordering case (c, i) => m += c -> (m.getOrElse(c, scala.collection.mutable.TreeSet()) += i) Error:(14, 80) not enough arguments for method apply: (implicit ord: scala.math.Ordering[A])scala.collection.mutable.TreeSet[A] in class SortedSetFactory. 错误:(14,80)分散scala.math.Ordering [T1]的隐式扩展,从对象中的方法Tuple9开始排序情况(c,i)=> m + = c - >(m.getOrElse(c,scala。 collection.mutable.TreeSet())+ = i)错误:(14,80)没有足够的方法适用于方法:(隐式ord:scala.math.Ordering [A])scala.collection.mutable.TreeSet [A] in class SortedSetFactory。 Unspecified value parameter ord. 未指定的值参数ord。 case (c, i) => m += c -> (m.getOrElse(c, scala.collection.mutable.TreeSet()) += i) case(c,i)=> m + = c - >(m.getOrElse(c,scala.collection.mutable.TreeSet())+ = i)

But if you uncomment line in main method it will be compiled successfully. 但是如果你在main方法中取消注释,它将被成功编译。 Why is this happenning? 为什么会这样?

Core of a problem 问题的核心

If you run this example with -Xlog-implicits (for sbt scalacOptions := Seq( "-Xlog-implicits" ) ) you will see a lot of [info] messages like this 如果你用-Xlog-implicits运行这个例子(对于sbt scalacOptions := Seq( "-Xlog-implicits" ) ),你会看到很多像这样的[info]消息

math.this.Ordering.Tuple6 is not a valid implicit value for scala.math.Ordering[A] because: [info] diverging implicit expansion for type scala.math.Ordering[T1] math.this.Ordering.Tuple6不是scala.math.Ordering [A]的有效隐式值,因为:[info]为scala.math.Ordering [T1]类型分散隐式扩展

this goes for basically every type in math.this.Ordering from simple like Boolean to Iterables and Options . 这基本上适用于math.this.Ordering所有类型。从简单的BooleanIterablesOptions

It tells us that compiler frantically trying to guess what type of TreeSet you are trying to construct when getOrElse fails. 它告诉我们编译器疯狂地试图猜测当getOrElse失败时你试图构造什么类型的TreeSet In your example it's just a TreeSet without any [explicit] typing so compiler has to find an implicit type to bind new TreeSet to. 在您的示例中,它只是一个没有任何[explicit]类型的TreeSet ,因此编译器必须找到一个隐式类型来绑定新的TreeSet By specifying Int in TreeSet constructor you will eliminate search for implicits and code will work without any magic . 通过在TreeSet构造函数中指定Int ,您将消除对implicits的搜索,并且代码将在没有任何魔法的情况下工作。

case ( c, i ) =>  m += c -> ( m.getOrElse( c, scala.collection.mutable.TreeSet[Int]() ) += i)

Now, to the main point. 现在,到了重点。

How compiler searches for implicits aka why is this magic worked 编译器如何搜索implicits,以及为什么这种魔法有效

You specified that m is a Map to TreeSet[Int] , that's great but .getOrElse will return a default value for m when it will fail to get a first character. 您指定m.getOrElse TreeSet[Int]Map ,这很好但是.getOrElse将在m无法获得第一个字符时返回m的默认值。 To construct that default value it will use definition provided by you 要构造该默认值,它将使用您提供的定义

scala.collection.mutable.TreeSet()

In the docs for .getOrElse we can see that for default value compiler will use a result yielded from of a second parameter computation. .getOrElse的文档中,我们可以看到 ,对于默认值,编译器将使用从第二个参数计算得到的结果。 Constructor of a TreeSet need to know what type of values you want to keep in that set and if it fails to see explicit definition it will look for implicits. TreeSet构造函数需要知道要在该集合中保留哪种类型的值,如果它未能看到显式定义,它将查找implicits。

new TreeSet()(implicit ord: Ordering[A])

As I mentioned in the begining it will bruteforce all the possible types in Ordering because, you know, it has to be ordering-oriented type but the tricky part is it doesn't know what type it actually needs. 正如我在开始时提到的那样,它将强制执行Ordering所有可能类型,因为,你知道,它必须是面向订单的类型,但棘手的部分是它不知道它实际需要什么类型。 Compiler has to construct a new TreeSet before it could += i to it. 编译器必须构造一个新的TreeSet才能+= i It can't just look forward, see that i is Int , construct a TreeSet[Int] and get back to add i to it. 它不能只是期待,看到iInt ,构造一个TreeSet[Int]并返回添加i That would be an actual magic. 这将是一个真正的魔术。

There is a great post on how does scala determines types for implicit parameters. 关于scala如何确定隐式参数的类型,有一篇很好的文章 Note that SortedSet is connected throught traits to TreeSet so it fails into category "some part of T". 请注意, SortedSet通过traits连接到TreeSet因此它无法进入类别“T的某个部分”。 In this particular case compiler will see an explicitly defined SortedTree[Int] and since there is no other option will use Int as implicit type for TreeSet . 在这种特殊情况下,编译器将看到一个显式定义的SortedTree[Int] ,因为没有其他选项将使用Int作为TreeSet隐式类型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM