56

Isn't toList a method that converts something into a List?

If yes so why can't I use parenthesis with it? I must be missing something more fundamental here.

Here is the example:

val l = Array(1,2,3).toList // works fine

val l = Array(1,2,3).toList() // gives the error below

Not enough arguments for method apply: (n: Int)Int in trait LinearSeqOptimized. Unspecified value parameter n.

Jatin
  • 31,116
  • 15
  • 98
  • 163
TraderJoeChicago
  • 6,205
  • 8
  • 50
  • 54
  • Found a similar question here which might help: [Confused about Scala method calling conventions, specifically the sum function on Seq](http://stackoverflow.com/questions/5545027/confused-about-scala-method-calling-conventions-specifically-the-sum-function-on) – jwinn Jul 10 '11 at 18:50

4 Answers4

72

If a method is defined as

def toList = { /* something */ }

then it must be called as

object.toList

with no extra parentheses. We say that this method has zero parameter lists.

We could also define a parameter list but put nothing in it:

def toList() = { /* something */ }

Now, we could call either of

object.toList()
object.toList

since Scala allows the shortcut of omitting parentheses on method calls.

As far as the JVM is concerned, there is no difference between the first definition ("zero parameter lists") and the second ("one empty parameter list"). But Scala maintains a distinction. Whether this is a good idea or not is debatable, but the motivation might be clearer when you realize that we can also

def toList()() = { /* something */ }

which is known as two empty parameter lists, and then call any of

object.toList()()
object.toList()
object.toList

and now, if we were to convert this into a function, we would type it as

() => () => T   /* T is the return value of the something */

while the second definition would be

() => T

which is clearly different conceptually, even if practically you use it the same way (put in nothing and sooner or later get out a T).

Anyway, toList doesn't need any parameters, and the Scala standard is to leave off the parens unless the method changes the object itself (rather than just returning something), so it's def toList without any parens afterwards. And thus you can only call it as object.toList.

Rex Kerr
  • 166,841
  • 26
  • 322
  • 407
  • 1
    Your explanation was great. It is hard for me to imagine the purpose of toList()(), but I will trust scala for now... – TraderJoeChicago Jul 10 '11 at 20:43
  • 8
    You forgot to mention that the extra parenthesis give an error because it's taken to be the `apply` method of the resulting `List`. – Daniel C. Sobral Jul 11 '11 at 00:19
  • @Daniel - Good point. But one could always fall back to ignoring parens if there was no apply, so that doesn't completely settle the issue. I'll let people read your comment for that explanation :) – Rex Kerr Jul 11 '11 at 14:41
  • Is (,) same as ()() in function?? – Jeff Lee Nov 19 '14 at 09:01
  • 1
    @JeffLee - No, `(,)` is invalid syntax. `()()` is two empty parameter lists (which only Scala knows about; to anything else it just looks like a method with no parameters). – Rex Kerr Nov 19 '14 at 12:13
  • There is "def addInt( a:Int, b:Int ) : Int" in there. – Jeff Lee Nov 20 '14 at 01:43
  • 1
    @JeffLee - Did you mean `(a: Int, b: Int)` vs. `(a: Int)(b: Int)`? Those are also not the same to Scala: one has one parameter list with two parameters, while the other has two parameter lists with one parameter each. (In bytecode they would be implemented the same way, though; the JVM doesn't have an idea of multiple parameter lists.) – Rex Kerr Nov 20 '14 at 04:06
  • 1
    @JeffLee - The Scala library uses the feature extensively for implicits and for making things look like built-in aspects of the language. For example, `list.foldLeft(0){ (sum, x) => sum + x }` is defined with two parameter lists (into which you put the initial value and function that incorporates another list element into the result). – Rex Kerr Nov 20 '14 at 20:18
  • The technique is called currying. It is also very useful when you don't have all parameters in hand. Say, you could have something like `sendMessage(msg: String)(timeout: Int)`. Then you might call `val sendMessage1sTimeout = sendMessage(1000)` and get a method that always times out after 1000 milliseconds. Then you can call `sendMessage1sTimeout("hello"); sendMessage1sTimeout("bye!)` for instance. This is particularly useful if a parameter is always going to be the same, or if your parameters are found at different places or points of execution. – Daniel Langdon Nov 26 '14 at 14:19
21

Your second line is actually interpreted as

val l = Array(1,2,3).toList.apply()

since foo(x) is "magic" syntax for foo.apply(x).

That's why the complier complains about "not enough arguments", as the apply method on lists takes one argument.

Thus you can write e.g.:

scala> val i = Array(1, 2, 3).toList(1)
i: Int = 2
Knut Arne Vedaa
  • 15,372
  • 11
  • 48
  • 59
5

Let me answer from Scala coding style perspective.

Scala style guide says...

Omit empty parenthesis, only be used when the method in question has no side-effects (purely-functional). In other words, it would be acceptable to omit parentheses when calling queue.size, but not when calling println().

Religiously observing this convention will dramatically improve code readability and will make it much easier to understand at a glance the most basic operation of any given method. Resist the urge to omit parentheses simply to save two characters!

Vishal John
  • 4,231
  • 25
  • 41
0

11 Years later... The exception Not enough arguments for method apply: (n: Int)Int in trait LinearSeqOptimized message gives you the clue. Checking the docs for LinearSeqOps.html#apply(n:Int):A:

def apply(n: Int): A Get the element at the specified index.

The toList in the Array doesn't take any argument [see Array:toList docs] and converts it to the l: List[Int], which is a LinearSeqOps trait:

val l = Array(1,2,3).toList        // l: List[Int] = List(1, 2, 3)

There are many ways to call apply(0) in the list l to get the element at index 0. They all do the same thing:

var el1 = l.apply(0)               // el1: Int = 1
el1 = l(0)                         // el1: Int = 1
el1 = Array(1,2,3).toList.apply(0) // el1: Int = 1
el1 = Array(1,2,3).toList(0)       // el1: Int = 1

But if we don't provide the Int argument to apply(n: Int), the code will fail with the given error. The same variations will all give the same error:

el1 = Array(1,2,3).toList()        // not enough arguments for method apply: (n: Int)
el1 = Array(1,2,3).toList.apply()  // not enough arguments for method apply: (n: Int)
el1 = l.apply()                    // not enough arguments for method apply: (n: Int)
el1 = l()                          // not enough arguments for method apply: (n: Int)
Ricardo
  • 3,696
  • 5
  • 36
  • 50