4

In this function "f" :

def f(x: => Int) : Int = x * x * x  //> f: (x: => Int)Int

var y = 0                           //> y  : Int = 0

f {
    y += 1
    println("invoked")
    y
}                                   //> invoked
                                    //| invoked
                                    //| invoked
                                    //| res0: Int = 6

"f" is invoked same amount of times as "x" parameter is multiplied.

But why is function invoked multiple times ?

Should "f" not expand to 1 * 1 * 1 not 1 * 2 * 3 ?

takendarkk
  • 3,347
  • 8
  • 25
  • 37
blue-sky
  • 51,962
  • 152
  • 427
  • 752

5 Answers5

4

Your x is not a function, it is a by-name parameter, and its type is a parameterless method type.

Parameterless method type means the same as def x, something that is evaluated every time you reference it. By reference, we mean x and not x.apply() or x().

The expression you're passing to your function f is evaluated every time x is referenced in f. That expression is the whole thing in braces, a block expression. A block is a sequence of statements followed by the result expression at the end.

Here's another explanation: https://stackoverflow.com/a/13337382/1296806

But let's not call it a function, even if it behaves like one under the covers.

Here is the language used in the spec:

http://www.scala-lang.org/files/archive/spec/2.11/04-basic-declarations-and-definitions.html#by-name-parameters

It's not a value type because you can't write val i: => Int.

It was a big deal when they changed the implementation so you could pass a by-name arg to another method without evaluating it first. There was never a question that you can pass function values around like that. For example:

scala> def k(y: => Int) = 8
k: (y: => Int)Int

scala> def f(x: => Int) = k(x)   // this used to evaluate x
f: (x: => Int)Int

scala> f { println("hi") ; 42 }
res8: Int = 8

An exception was made to "preserve the by-name behavior" of the incoming x.

This mattered to people because of eta expansion:

scala> def k(y: => Int)(z: Int) = y + y + z
k: (y: => Int)(z: Int)Int

scala> def f(x: => Int) = k(x)(_)  // normally, evaluate what you can now
f: (x: => Int)Int => Int

scala> val g = f { println("hi") ; 42 }
g: Int => Int = <function1>

scala> g(6)
hi
hi
res11: Int = 90

The question is how many greetings do you expect?

More quirks:

scala> def f(x: => Int) = (1 to 5) foreach (_ => x)
f: (x: => Int)Unit

scala> def g(x: () => Int) = (1 to 5) foreach (_ => x())
g: (x: () => Int)Unit

scala> var y = 0
y: Int = 0

scala> y = 0 ; f { y += 1 ; println("hi") ; y }
hi
hi
hi
hi
hi
y: Int = 5

scala> y = 0 ; g { y += 1 ; println("hi") ; () => y }
hi
y: Int = 1

scala> y = 0 ; g { () => y += 1 ; println("hi") ; y }
hi
hi
hi
hi
hi
y: Int = 5

Functions don't cause this problem:

scala> object X { def f(i: Int) = i ; def f(i: => Int) = i+1 }
defined object X

scala> X.f(0)
res12: Int = 0

scala> trait Y { def f(i: Int) = i }
defined trait Y

scala> object X extends Y { def f(i: => Int) = i+1 }
defined object X

scala> X.f(0)
<console>:11: error: ambiguous reference to overloaded definition,
both method f in object X of type (i: => Int)Int
and  method f in trait Y of type (i: Int)Int
match argument types (Int)
              X.f(0)
                ^

Compare method types:

http://www.scala-lang.org/files/archive/spec/2.11/03-types.html#method-types

This is not a pedantic distinction; irrespective of the current implementation, it can be confusing to think of a by-name parameter as "really" a function.

Community
  • 1
  • 1
som-snytt
  • 39,429
  • 2
  • 47
  • 129
  • A very good explanation of by-name parameters is given in section 9.5 of [Programming in Scala](http://www.amazon.com/Programming-Scala-Comprehensive-Step-Step/dp/0981531644). There the author explains that a function value is actually created whose `apply` method will evaluate the expression (in this case the block expression). So, while the expression passed to `f` isn't really a function, what `f` evaluates actually is one. – melston Jul 17 '14 at 01:08
  • @melston No, you're mistaken. The authors use a function param as a teaching device, but they never say that a by-name is a function, because that would be wrong. http://www.artima.com/pins1ed/control-abstraction.html#9.5 Then they say "a function value is created" at the end of the section, they ran out of metaphors and started describing implementation details, not the language. Some people may wish it were specified that way. – som-snytt Jul 17 '14 at 04:08
  • " a by-name is not "really" a function except in name". How does the behaviour differ from a zero-parameter function, then? It seems to me that in the OP's case, the observed behaviour would be the same – The Archetypal Paul Jul 17 '14 at 07:02
  • @Paul 'a' + 1 equals 98. But that doesn't mean a Char is the same as an Int. Try using =>A where you need ()=>A, for instance. – som-snytt Jul 17 '14 at 20:24
  • Right. But I don't see why you're so vehement here. it seems mostly syntactic sugar to me (you can write `x` without the need for `x()` at the use, and you don't need to wrap the calling expression in `{}`. – The Archetypal Paul Jul 17 '14 at 20:34
  • @Paul I know, it sounds crazy. It's the feature interactions that make it a useful distinction, as opposed to syntax. Also, the type thing. – som-snytt Jul 17 '14 at 23:19
1

Another way of saying what has already been said is that inside f you invoke the function x three times. The first time it increments the y var and returns 1. The second time it again increments y returning 2 and the third time it again increments y and returns 3.

If you want it invoked only once then you may want to do something like this:

def f(x: => Int) : Int = x * x * x
var y = 0

lazy val xx = {
    y += 1
    println("invoked")
    y
}

f {xx}

This will print 'invoked' only once and result in a returned value of 1.

melston
  • 2,198
  • 22
  • 39
  • but x is not a function, it is just a parameter ? – blue-sky Jul 16 '14 at 22:15
  • 1
    Sorry, no. `x` (in `f`) is defined as a function taking no parameters and returning an `Int`. What you have is a shorthand for `f(x: () => Int) : Int ...` where the `()` implies `Unit`. – melston Jul 16 '14 at 22:26
  • 1
    No, that's wrong. `=> Int` is *not* shorthand for `() => Int`. It's called a parameterless method type because it has no parameters, not an empty param list. – som-snytt Jul 17 '14 at 04:18
1

x: T means need a T value.

x: => T means need a T value, but it is call by name.

x: () => T This means need a function given nothing to T

However, this question is not related to the difference between function and method.

The reason is call by name is invoked every time you try to use it.

change to call by value def f(x: Int) : Int, it will only invoke once.

Xiaohe Dong
  • 4,953
  • 6
  • 24
  • 53
0

The result which your function f() returns is changing, because there is a global variable that is incremented with every subsequent call to that function.

the x in f(x: => Int) is interpreted as "some function that returns Int". So it has to be called 3 times to evaluate the x*x*x expression. With every call, you increment the global variable and return the result, which is how you arrive at three subsequent natural numbers (because the global variable is initialized to 0). Hence 1*2*3.

Ashalynd
  • 12,363
  • 2
  • 34
  • 37
0

Because you increment y by 1 every time the argument is used inside f

Alireza
  • 4,976
  • 1
  • 23
  • 36