3

Consider the following 2 objects

object TestObj1 {
  def testMethod = "Some text"
}

object TestObj2 {
  def testMethod() = "Some text"
}

and if I call those methods directly, they do what I expect

scala> TestObj1.testMethod
res1: String = Some text

scala> TestObj2.testMethod
res2: String = Some text

But now if we define following function

def functionTakingFunction(callback: () => String) {
  println("Call returns: " + callback())
} 

and try to call it, the method defined without () is not accepted.

scala> functionTakingFunction(TestObj1.testMethod)
<console>:10: error: type mismatch;
 found   : String
 required: () => String
              functionTakingFunction(TestObj1.testMethod)
                                              ^

scala> functionTakingFunction(TestObj2.testMethod)
Call returns: Some text

I also noticed that you can't call the TestObj1.testMethod using parentheses, since it already is a String. But what is causing this behavior?

Antti Vikman
  • 97
  • 1
  • 9
  • 2
    Just to point out, @om-nom-nom answer is right (it gets things done), still, the reason for the type mismatch is that `def foo` and `def foo()` are two different mammals in Scala... While they are both **0-arity** methods, empty argument list and no argument list wilds different results, see http://stackoverflow.com/questions/7409502/what-is-the-difference-between-def-foo-and-def-foo-in-scala. – Anthony Accioly Aug 20 '13 at 18:14
  • Those aren't functions, they are methods. – Jörg W Mittag Aug 20 '13 at 22:29
  • @JörgWMittag my bad on the naming. But would it make a difference if they were functions? And the functionTakingFunctiondoesn't know are you passing a function or a method, right? – Antti Vikman Aug 21 '13 at 08:04
  • Yes, it would make a difference. Functions are objects, methods aren't. You *cannot* pass a method to a method, precisely because it *isn't* an object. You can only pass functions to methods. If you want to do anything with a method, you have to η-expand it using `_`. Scala will *sometimes* perform *implicit η-expansion*, *if* it can clearly see that you do *not* actually want to call the method but convert it to a function and pass it as an argument. But in your first example it can *not* clearly see that, because that's a legal method call. – Jörg W Mittag Aug 21 '13 at 08:18
  • @AnthonyAccioly post you linked shares quite a bit of clarity on what is happening in the background, thanks. – Antti Vikman Aug 21 '13 at 08:18

3 Answers3

9

You are not passing the method, you're invoking it and then passing its result into the call.

If you want to pass the method, you first have to convert it into a function, and pass that:

functionTakingFunction(TestObj1.testMethod _)

Here the testMethod is converted into a partially applied function (said to be "tied").

The reason you have to do that is because TestObj1.testMethod does not evaluate to function0 (which is what need to be passed to functionTakingFunction), but to a String because of the way it is declared (without parentheses).

It works on TestObj2 because the testMethod is defined with parentheses and thus just typing TestObj2.testMethod does not invoke it.

corazza
  • 31,222
  • 37
  • 115
  • 186
om-nom-nom
  • 62,329
  • 13
  • 183
  • 228
  • 3
    `testFunction` is not a `Function`, it is a method. Partial application when used with methods yields `FunctionN` (where N is the number of arguments to the method from which it was derived). It doesn't help understanding to blur the distinction between non-first-class entities (methods) and first-class function values. – Randall Schulz Aug 20 '13 at 16:41
  • @RandallSchulz quite fair, can you write your own answer, please? (I will remove mine then) – om-nom-nom Aug 20 '13 at 16:43
  • @Randall. `TestObj1.testFunction2` is also evaluated as `function0`, to all practical needs, the problem here is that `testFunction` is evaluated as a String, so, despite the fact that `testFunction` and `testFunction2` are really methods, om-nom-nom solution is right ([Working Example](http://ideone.com/Hwo1Bq)) – Anthony Accioly Aug 20 '13 at 18:01
  • This solves the issue, it provides explanation and hints the right terms to search for more info, so thank you from the great answer. – Antti Vikman Aug 21 '13 at 08:16
  • I didn't say his code did not do what's intended, I just think people should understand what's what and what's really happening with that code. – Randall Schulz Aug 21 '13 at 12:58
0

functionTakingFunction(TestObj1.testFunction) is called as functionTakingFunction("Some text") - it is evaluated rather than passed

monnef
  • 3,903
  • 5
  • 30
  • 50
-1

Here, you are not passing the function. What you are trying to do is evaluate TestObj1.testFunction and then pass that result to functionTakingFunction But if you see the definition of functionTakingFunction, it says clearly

def functionTakingFunction(callback: () => String)

means this function expects a function with Return Type as Unit. But TestObj1.testFunction is having no return type.

The difference between Unit Return Type and No Return Type is:

  • No Return Type means this method will not return anything
  • Unit Return Type means this method will return something which is having no meaningful value.

Hope it helps you now. Happy coding and enjoy Scala.

Sudipta Deb
  • 1,040
  • 3
  • 23
  • 43
  • This answer confuses me on so many levels. First is there actually a case in Scala where I would not get a return at all (meaning not even Unit). And second doesn't the "=> String" exactly mean that it expects function with return type as String. – Antti Vikman Aug 21 '13 at 08:11
  • 1
    "means this function expects a function with Return Type as Unit." – No, it means it expects a function with return type `String`. "`TestObj1.testFunction` is having no return type." – Wrong, it has return type `String`. There is no such thing as "no return type" in Scala. Everything has a return type. Everything *must* have a return type, since everything is an expression and thus everything returns *something*. – Jörg W Mittag Aug 21 '13 at 08:23