背景
我最近参加了一个Scala初学者聚会,我们正在讨论方法和函数之间的差异(这个问题也在这里深入探讨)。
例如:
scala> val one = 1
one: Int = 1
scala> val addOne = (x: Int) => x + 1
addOne: Int => Int = <function1>
这表明vals不仅可以是整数类型,还可以是函数类型。我们可以在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方法的对象,并且如何去除语法糖以展示这一点:
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中也能使用相同的:type技巧:
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中不起作用?有没有一种方法来去掉语法糖,并演示到底发生了什么?
- 如果方法不能被提升的原因是由于完整的签名“implicit”,那么具体是怎么回事呢?
最后,有没有一种强大的方式从REPL中检查类型和签名?