Scala为何无法推断省略括号与点的方法类型?附代码实例
Great question! Let's break down the two parts of your problem to understand why Scala's type inference behaves differently in these cases.
1. Why type inference fails for the infix :+ call (m.getOrElse(string(i), Vector()) :+ i)
Let's start with the core issue here: when you write Vector() without a type parameter, Scala infers its type as Vector[Nothing] by default (since Nothing is Scala's bottom type—its subtype of every other type). Here's the step-by-step breakdown of why the infix call breaks:
- First, Scala evaluates
m.getOrElse(string(i), Vector()). ThegetOrElsemethod has this signature:
For your map,def getOrElse[B >: V](key: K, default: => B): BVisVector[Int]. Since you passVector[Nothing]as the default, Scala infersBasVector[Nothing](becauseVector[Nothing]is a subtype ofVector[Int], satisfyingB >: V). - Next, you try to call
:+ ion thisVector[Nothing]. The:+method forVectorlooks like this:
Fordef :+(elem: A): Vector[A]Vector[Nothing],AisNothing—so this method expects an element of typeNothing, but you're passing anInt. SinceIntisn't a subtype ofNothing, this causes a type mismatch, and inference fails.
The key here is that infix expressions are evaluated left-to-right, so Scala can't use the context of the :+ i call to retroactively infer a more specific type for Vector() earlier in the chain.
2. Why the dot notation works (m.getOrElse(string(i), Vector()).:+(i))
When you use dot notation to call :+, Scala can leverage target type inference—it looks at the expected type of the entire expression to guide inference. Here's what happens:
- The entire expression
m.getOrElse(...).:+(i)needs to produce aVector[Int](since it's being used as the value in yourMap[Char, Vector[Int]]). - Scala knows that
.:+(i)(withi: Int) returns aVector[Int], so the receiver of this method call (the result ofm.getOrElse(...)) must be aVector[Int]. - Going back to
getOrElse, Scala now infersBasVector[Int](matching the expected return type), so it treatsVector()asVector[Int]instead ofVector[Nothing]. This aligns perfectly with the:+ icall, and everything works as intended.
A Simple Fix for the Infix Case
If you want to use infix notation and avoid the type inference issue, just explicitly specify the type parameter for Vector:
def indexes(string: String): Map[Char, Vector[Int]] = (0 until string.length).foldLeft(Map[Char, Vector[Int]]()){ (m, i) => m + (string(i) -> (m.getOrElse(string(i), Vector[Int]()) :+ i)) }
By writing Vector[Int]() instead of Vector(), you remove the ambiguity for Scala, and the infix call works without problems.
内容的提问来源于stack exchange,提问作者Andrei Iatsuk

