6

I'm using Scala 2.11 . If I create a function like :

def func1 (a: Int* ) : Int = a.reduce(_+_)

I call it using

func1(1,2,3,4)
// 10
func1(Seq(1,2,3,4) : _*)
//10

which is fine.

But when I try to define a function literal like :

val func2:(Int*) => Int = _.reduce(_+_)

I get an error saying :

<console>:5: error: type mismatch;
 found   : Int* => Int
 required: Seq[Int] => Int
  lazy val $result = INSTANCE.`func2`

Why does it want a Seq[Int] in the second case but not in first though the definition is the same?

How are the varargs being passed in the first case such that reduce is able to get invoked over them?

philantrovert
  • 9,904
  • 3
  • 37
  • 61
  • 1
    Possible duplicate of [Error with varargs for function-objects in Scala?](https://stackoverflow.com/questions/8623126/error-with-varargs-for-function-objects-in-scala) – Suma Sep 06 '17 at 10:02

1 Answers1

5

Scala makes a distinction between methods and functions. Your func1 is a method whose parameter is repeated, and this is desugared under the hood into a simple Seq. On the other hand, your func2 is a function, and repeated parameters are not allowed in that context.

If the compiler expects a function but is given a method instead, it performs eta-expansion. For example, method def(s: String) = s is turned into a function (s: String) => s. I'm saying this to make a point that distinction between methods and functions is very clear and very important.

Note that the link I provided says "Function Declarations and Definitions", which is a kind of clumsy naming, since it's actually talking about methods, not functions. Function is a value just like any other and it can be e.g. kept in a collection or returned from another function. Methods can't.

One final comment and I'm done: don't make a common mistake and think "def is method, val is function". Function can also be defined using a def instead of val, in which case it's still a function value, but it's evaluated at the point of usage instead of point of declaration. Best way to differentiate methods from functions is to check for the parameters: def foo(someParameter: X): Y is a method. Functions cannot have the "(someParameter: X)" part. Instead, function is just a value, and its type is X => Y:

// method
def foo(x: X): Y = // some code that returns Y

// function
def foo: X => Y = (x: X) => // some code that returns Y

I deliberately used def for function too to make a point (to prevent readers from making the val-def mistake), but it's more common to define functions using val.

slouc
  • 9,508
  • 3
  • 16
  • 41
  • Your distinction between method and function is subjective. Can you provide documentation backing your assumption? Btw I don't agree with it :) You second `foo` could be "a method that returns a function" or "a function that returns a function" – pedromss Sep 06 '17 at 10:08
  • Thank you for the explanation. – philantrovert Sep 06 '17 at 10:08
  • I do have another question. You say the varargs call _is desugared under the hood into a simple `Seq`_, then why can't I directly pass a `Seq(1,2,3)` to `func1` ? – philantrovert Sep 06 '17 at 10:12
  • @pedromss See 3.3.1: Method Types in the [spec](http://www.scala-lang.org/docu/files/ScalaReference.pdf). "Parameterless methods name expressions that are re-evaluated each time the parameterless method name is referenced". So yeah, we could debate over who's technically right here; I'm gonna say it's you. But still, reasoning one way or another makes no difference in this case because they are trivially isomorphic. Distinction is much more important in case of methods with parameters. – slouc Sep 06 '17 at 10:14
  • 1
    @philantrovert Because those are two different things; `Seq(1, 2, 3)` is a single value of type `Seq[Int]`, and `func1` expects one or more values of type `Int`. If you want to pass it, you need to add _* to the end (as described in the spec). So `func1(Seq(1,2,3): _*)`. This converts the "one value of type Seq[Int]" into "several separate values of type Int". Once you're inside, it's all converted back to Seq, but we don't see that. – slouc Sep 06 '17 at 10:16
  • @slouc Great. I think I got it now. I created `def foo(x: Int*) = Array(x)` and after I called it, it returned an `Array[Seq[Int]]`. That is in accordance with what you explained right? – philantrovert Sep 06 '17 at 12:24
  • philantrovert Yes, looks that way. TBH I never really poked around varargs too much, but yeah, it makes sense. `Int*` is not really a type. – slouc Sep 06 '17 at 14:00