Scala Map 的获取与应用操作:"type mismatch"

Scala Map's get vs apply operation: "type mismatch"

我正在学习 Scala 并发现以下内容:

List(('a', 1)).toMap get 'a'           // Option[Int] = Some(1)
(List(('a', 1)).toMap) apply 'a'       // Int = 1
(List(('a', 1)).toMap)('a')            // Error: type mismatch;
                                          found   : Char('a')
                                          required: <:<[(Char, Int),(?, ?)
                                          (List(('a', 1)).toMap)('a')

但随后将其分配给一个变量又可以工作了。

val b = (List(('a', 1)).toMap)
b('a') // Int = 1

为什么会这样?

标准文档给出:

ms get k

The value associated with key k in map ms as an option, None if not found.

ms(k) (or, written out, ms apply k)

The value associated with key k in map ms, or exception if not found.

为什么第三行不起作用?

signature略有不同:

abstract def get(key: K): Option[V]

def apply(key: K): V

问题是错误处理:get 将在未找到元素时 return None 并且 apply 将抛出异常:

scala> Map(1 -> 2).get(3)
res0: Option[Int] = None

scala> Map(1 -> 2).apply(3)
java.util.NoSuchElementException: key not found: 3
  at scala.collection.immutable.Map$Map1.apply(Map.scala:111)
  ... 36 elided

关于失败行:toMap 有一个隐式参数 ev: A <:< (K,V) 表示类型约束。当您调用 r.toMap('a') 时,您正在为隐式传递一个显式值,但它的类型错误。 Scala 2.13.0 有一个伴生对象 <:<,它提供了自反性方法(使用给定类型本身而不是适当的子类型)。现在以下作品:

scala> List(('a', 1)).toMap(<:<.refl)('a')
res3: Int = 1

备注:我无法在 Scala 2.12.7 中调用 <:<.refl,添加似乎是最近的。

它本质上只是隐式参数与 apply-语法糖和奇怪的括号-消除行为的特殊冲突。

explained here

中的括号
(List(('a', 1)).toMap)('a')

被过早丢弃,所以你最终得到

List(('a', 1)).toMap('a')

以便编译器尝试将 'a' 解释为某些未知类型 ??.

的隐含证据 (Char, Int) <:< (?, ?)

这在这里有效(它没有用,它只是为了演示编译器通常在这个位置期望什么):

(List(('a', 1)).toMap(implicitly[(Char, Int) <:< (Char, Int)]))('a')

List(...).toMap 赋值给变量也有效:

({val l = List((1, 2)).toMap; l})(1)

或者,您可以强制 toMap 停止接受参数,方法是将参数提供给 identity 不执行任何操作的函数:

identity(List((1, 2)).toMap)(1)

但是消除隐式参数和 apply 语法糖歧义的最简单明了的方法是直接写出 .apply

List((1, 2)).toMap.apply(1)

我认为在这一点上应该很明显为什么 .get 表现不同,所以我不会详细说明。