1

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 the 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.

2 Answers 2

6

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.

Sign up to request clarification or add additional context in comments.

Comments

1

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).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.