Reputation: 1124
Consider the following code:
(define f2!
(let([n 0])
(lambda()
(set! n (add1 n))
n)))
(f2!)
(f2!)
I would expect the output to be 1 1
, since every time we call f2!
, n
will be redefined to 0.
But the output is: 1 2
. Why is that?
Upvotes: 1
Views: 88
Reputation: 3017
let
is a syntactic sugar for an applied lambda, so it might be easier for you to see the reason for this using the expended form for let:
(define f2!
((lambda (n)
(lambda () (set! n (add1 n)) n))
0))
So when you define f2!
, you apply a lambda (2nd line) while binding its parameter, n
, to the scheme object 0
(4th line). The definition of f2!
is the result of that application, another lambda object.
That internal lambda has an environment which contains the parameters of the first lambda (namely n
). Not a copy of those parameters, but and actual reference to them, so when you apply that internal lambda and set!
the value of n
, you're setting the value of the referenced n
as was bound when you first defined f2!
.
TL;DR, you defined f2!
once, at which point you bound n
, and from that point on you're getting and setting that same n
.
Upvotes: 1
Reputation: 48745
You have made a function that is closed over a variable. In short this is a poor mans OO.
When you call f1!
the same free binding n
(instance variable) will be mutated and returned. You have created a counter.
You can do the same in many languages since most of them are lexical. eg. JS:
const fBang = (() => {
let n = 0;
return () => ++n;
})();
console.log(fBang()); // prints 1
console.log(fBang()); // prints 2
Upvotes: 2