为什么Scala不允许List.map _并在REPL中键入签名

时间:2013-09-11 21:27:20

标签: scala

背景

我最近参加了初学者Scala聚会,我们讨论的是方法和功能之间的区别(也在深入讨论here)。

例如:

scala> val one = 1
one: Int = 1

scala> val addOne = (x: Int) => x + 1
addOne: Int => Int = <function1>

这表明val不仅可以具有整数类型,而且可以具有函数类型。我们可以在scala repl中看到类型:

scala> :type addOne
Int => Int

我们还可以在对象和类中定义方法:

scala> object Foo {
 |   def timesTwo(op: Int) = op * 2
 | }
defined module Foo

虽然方法没有类型(但是有一个类型签名),我们可以将它提升到一个函数中,看看它是什么:

scala> :type Foo.timesTwo
<console>:9: error: missing arguments for method timesTwo in object Foo;
follow this method with `_' if you want to treat it as a partially applied function
          Foo.timesTwo
              ^


scala> :type Foo.timesTwo _
Int => Int

到目前为止,这么好。我们甚至讨论了函数实际上是如何使用apply方法的对象,以及我们如何使用de-syntactic sugarify表达式来表明这一点:

scala> Foo.timesTwo _ apply(4)
res0: Int = 8

scala> addOne.apply(3)
res1: Int = 4

对我而言,这对学习语言非常有帮助,因为我可以内化语法实际所暗示的内容。

有问题的例子

但是,我们遇到了一个我们无法识别的情况。以一系列字符串为例。我们可以在函数上映射函数,演示基本的Scala集合和函数式编程:

scala> List(1,2,3).map(_*4)
res2: List[Int] = List(4, 8, 12)

好的,那么List(1,2,3).map()的类型是什么?我希望我们也会这样做:在repl中键入技巧:

scala> :type List(1,2,3).map _
<console>:8: error: Cannot construct a collection of type Nothing with elements of type Nothing based on a collection of type List[Int].
          List(1,2,3).map _
                      ^

从API定义中,我知道签名是:

def map[B](f: (A) ⇒ B): List[B]

但也有一个完整的签名:

def map[B, That](f: (A) ⇒ B)(implicit bf: CanBuildFrom[List[A], B, That]): That

问题

所以有两件事我不太明白:

  • 为什么正常的函数提升技巧不适用于List.map?有没有办法解决错误的陈述,以证明正在发生的事情?
  • 如果无法取消该方法的原因是由于完全签名“隐含”,那究竟是什么?

最后,是否有一种可靠的方法来检查REPL中的类型和签名?

2 个答案:

答案 0 :(得分:7)

您遇到的问题与Scala中functions are monomorphic, while methods can be polymorphic的事实有关。因此,必须知道类型参数BThat才能为List.map创建函数值。

编译器尝试推断出参数,但无法提出任何合理的参数。如果您提供参数,您将获得有效的函数类型:

scala> List(1,2,3).map[Char, List[Char]] _
res0: (Int => Char) => List[Char] = <function1>

scala> :type res0
(Int => Char) => List[Char]

答案 1 :(得分:1)

如果没有实际的函数参数,函数的推断类型为Int => Nothing,但目标集合也是Nothing。范围内没有合适的CanBuildFrom[List[Int], Nothing, Nothing],我们可以通过在REPL中输入implicitly[CanBuildFrom[List[Int], Nothing, Nothing]]来看到(出现相同的错误)。如果提供类型参数,则可以获得一个函数:

scala> :type List(1,2,3).map[Int, List[Int]] _
(Int => Int) => List[Int]

我认为你不能检查REPL中的方法签名。这就是Scaladoc的用途。