Scala currying with 'byname' parameters and non-'byname' parameters
I have (had) an assumption that appears to be wrong. My 'main' language in the past has been C++ and in C++ I could do function parameter binding like this in pseudo:
// declaration
void f(int param1, int param2);
// usage
func<void, int> boundfunc = bind(f, 1, _1)
func(2) // equivalent to f(1, 2)
The interesting bit is that if I replace 1
in the definition of boundfunc
with a function call, then that function call is invoked at the bind site, not the call to func
.
So, when trying to solidify the concept of currying in my head, I compare it with binding in C++ and I assume (incorrectly) that currying with a non-'byname' parameter would equate to the same idea - called at the 'bind site'. Of course, this isn't what's happening, and I'm curious as to why f
and g
below behave the same. I'm obviously missing something...
import scala.util.Random
class X { println("Constructing"); val r = new Random }
def f(limit: Int)(x: X) = x.r.nextInt(limit)
def g(limit: Int)(x: => X) = x.r.nextInt(limit)
def r = f(100)(new X) // Doesn't print "Constructing"
def s = g(100)(new X) // Doesn't print "Constructing"
r // prints "Constructing"
r // prints "Constructing"
s // also prints "Constructing"
s // also prints "Constructing"
I expect the definition of s
to do what it's doing (i.e. not call 'new X') but I did expect the definition of r
to call it due to the fact that f
has a basic definition of the x
parameter (i.e. non-'byname').
Now, as I said, there's clearly some basic FP concept I'm missing, which I hope to rectify soon, but I'd also like to know if there's a way to get the equivalent of parameter binding so that new X
isn't called repeatedly.
(Edit after answer from barjak)
Indeed, the problem stems from my misuse of def
in the creation of r
and s
- I should be using val
, which makes the call to new X
happen at the 'bind site'.
The last surprising thing is that the 'byname' parameter in g
is resolved only once, when we create s
, as opposed to when we evaluate s
explicitly. (i.e. "Constructing" is only printed when we define r
and s
as opposed to when we define r
and when we evaluate s
).
Now, my assumption is that Scala's doing the properly defined thing and binding x
to th开发者_Go百科e result of the second parameter and thus it doesn't matter, in the context of s
or r
whether x
is 'byname' or not.
I suppose, if I wanted to evaluate x
repeatedly, I would need more than a 'byname' parameter, and would require an explicit function parameter instead, which would evaluate repeatedly when evaluating s
.
r
is declared with the keyword def
, which actually means "reevaluate the expression for each call". I think you want to use the val
keyword, which will evaluate the expression only once for each instanciation of X
.
val r = f(100)(new X)
val s = g(100)(new X)
Otherwise, I think your interpretation is right.
As far as I understood by name parameters there is no great difference between f
and g
in
def f(limit: Int)(x: X) = x.r.nextInt(limit)
def g(limit: Int)(x: => X) = x.r.nextInt(limit)
because of evaluating x
in body definitions only once.
But if you had a loop or so inside definition x
could be evaluated more than one ... and then in f
a new X
would be evaluated only one time (before method call) but in g
a new X
is evaluated as often as the loop runs.
/* using x in body twice (for demonstration only) */
def f(limit: Int)(x: X) = { x.r.nextInt(limit); x.r.nextInt(limit) }
def g(limit: Int)(x: => X) = { x.r.nextInt(limit); x.r.nextInt(limit) }
val r = f(100)(new X) /* prints "Constructing" only one time */
val s = g(100)(new X) /* prints "Constructing" twice */
r /* Doesn't print "Constructing" */
r /* Doesn't print "Constructing" */
s /* Doesn't print "Constructing" */
s /* Doesn't print "Constructing" */
And finnally to bring currying into play perhaps you would like to bind new X
to x
which you can do with a swap of parameter lists in function definition. Then you could do
def f(x: X)(limit: Int) = x.r.nextInt(limit)
val r = f(new X)_
val s = f(new X)_
r(100)
r(200)
s(100)
s(200)
where r
and s
are two different (independent) random streams constructed with currying f
(no need of by name parameters).
精彩评论