I want a function that keeps local state in Ruby.
That word "function" should immediately raise a big fat red flashing warning sign that you are using the wrong programming language. If you want functions, you should use a functional programming language, not an object-oriented one. In a functional programming language, functions usually close over their lexical environment, which makes what you are trying to do absolutely trivial:
var state;
function incMult(factor) {
if (state === undefined) {
state = 0;
}
state += 1;
return factor * state;
}
print(incMult(2)); // => 2
print(incMult(2)); // => 4
print(incMult(2)); // => 6
This particular example is in ECMAScript, but it looks more or less the same in any functional programming language.
[Note: I'm aware that it's not a very good example, because ECMAScript is actually also an object-oriented language and because it has broken scope semantics that atually mean that state
leaks in this case, too. In a language with proper scope semantics (and in a couple of years, ECMAScript will be one of them), this'll work as intended. I used ECMAScript mainly for its familiar syntax, not as an example of a good functional language.]
This is the way that state is encapsulated in functional languages since, well, since there are functional languages, all the way back to lambda calculus.
However, in the 1960s some clever people noticed that this was a very common pattern, and they decided that this pattern was so common that it deserved its own language feature. And thus, the object was born.
So, in an object-oriented language, instead of using functional closures to encapsulate state, you would use objects. As you may have noticed, methods in Ruby don't close over their lexical environment, unlike functions in functional programming languages. And this is precisely the reason: because encapsulation of state is achieved via other means.
So, in Ruby you would use an object like this:
inc_mult = Object.new
def inc_mult.call(factor)
@state ||= 0
@state += 1
factor * @state
end
p inc_mult.(2) # => 2
p inc_mult.(2) # => 4
p inc_mult.(2) # => 6
[Sidenote: This 1:1 correspondence is what functional programmers are talking about when they say "objects are just a poor man's closures". Of course, object-oriented programmers usually counter with "closures are just a poor man's objects". And the funny thing is, both of them are right and neither of them realize it.]
Now, for completeness' sake I want to point out that while methods don't close over their lexical environment, there is one construct in Ruby, which does: blocks. (Interestingly enough, blocks aren't objects.) And, since you can define methods using blocks, you can also define methods which are closures:
foo = Object.new
state = nil
foo.define_singleton_method :inc_mult do |factor|
state ||= 0
state += 1
factor * state
end
p foo.inc_mult(2) # => 2
p foo.inc_mult(2) # => 4
p foo.inc_mult(2) # => 6