4

Background

I recently attended a beginner Scala meetup and we were talking about the difference between methods and functions (also discussed in-depth here).

For example:

scala> val one = 1
one: Int = 1

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

This demonstrates that vals can not only have an integer type, but can have a function type. We can see the type in the scala repl:

scala> :type addOne
Int => Int

We can also define methods in objects and classes:

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

And while a method doesn't have a type (but rather is has a type signature), we can lift it into a function to see what it is:

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

So far, so good. We even talked about how functions are actually objects with an apply method and how we can de-syntactic sugarify expressions to show this:

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

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

To me, this is quite helpful in learning the language because I can internalize what the syntax is actually implying.

Problematic Example

We did, however, run into a situation that we could not identify. Take, for example, a list of strings. We can map functions over the values demonstrating basic Scala collections and functional programming stuff:

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

Ok, so what is the type of List(1,2,3).map()? I would expect we would do the same :type trick in the 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 _
                      ^

From the API definition, I know the signature is:

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

But there is also a full signature:

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

Question

So there are two things I don't quite understand:

  • Why doesn't the normal function lift trick work with List.map? Is there a way to de-syntactic sugar the erroneous statement to demonstrate what is going on?
  • If the reason that the method can't be lifted is due to the full signature "implicit", what exactly is going on there?

Finally, is there a robust way to inspect both types and signatures from the REPL?

4

2 回答 2

7

您遇到的问题与以下事实有关:在 Scala 中,函数是单态的,而方法可以是多态的。因此,类型参数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]
于 2013-09-11T21:57:24.880 回答
1

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

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

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

于 2013-09-11T21:58:44.253 回答