From: Garthy D Date: 2013-02-17T12:11:26+09:00 Subject: Re: Fun with finalizers Hi Robert, Thankyou very much, yet again. An excellent and incredibly informative response, as always. The hole in my understanding (and what I had begun to suspect was the case, and I think you have identified) was pretty-much here: > The closure captures the current scope, i.e. all local variables. Before I had encountered the problem, my understanding was that the closure would capture any referenced variables similarly to a function/method call. I wasn't 100% sure of the mechanics, but I believed that it "just happened". What I didn't understand was that it did this by holding on to the entire scope- and just that one scope. The distinction had not become apparent to me as my understanding did not clash with what was actually happening when finalizers were not involved. However, the differences that arise once finalizers enter the picture are actually very significant. I think I did not pick this up early as a consequence of this means that most of the discussion online regarding Ruby finalizers either glosses over this point- or flat out misses it. There are plenty of mentions of not implicitly including the object being finalized in the finalizer (by, say, expecting to be able to call a method on the finalized object), but I'm not sure I've seen a mention of capturing the current scope and needing to be careful with the visible variables in it. However, a common solution seems to be to use a method to return the finaliser proc itself, and I'd missed the distinction that by doing it this way, the call is created with a different scope than that which actually sets the finalizer itself. Thus the finalizer never even sees the value being finalized, avoiding the problem nicely. The first example you have given makes it completely clear what is happening. Based on my previous understanding, I would be unsure of what the output from "g.call" would be. My first two guesses would probably have been an exception, or possibly one or zero, but at that point I'd be questioning if my understanding was actually correct. Knowing what I know now, the answer is obvious, even trivial. Note that if the example didn't use an integer, but an object, it would have fit in with my previous understanding. Only by being an immediate value did the flaw in my previous understanding become apparent. Thankyou for taking the time to put together yet another superb post for the list. I am frequently in awe at the level of detailed knowledge you have in some of the more complex mechanics in Ruby. I hope that people encountering similar issues can also stumble across it, so that the post ends up helping considerably more people than just myself. Cheers, Garth On 17/02/13 01:00, Robert Klemme wrote: > On Sat, Feb 16, 2013 at 12:08 PM, Garthy D > wrote: > >> Excellent thinking. I also thought it might be something along those lines >> too. > > To make it crystal clear: the reason is that there is a closure > involved. The closure will hold on to the object referenced by a on > method entry - unless, as you discovered, that reference is cleared. > > Just in case and if you don't know, here's what a closure does: > > irb(main):015:0> def f; x=0; lambda { x+=1 } end > => nil > irb(main):016:0> g = f > => # > irb(main):017:0> g.call > => 1 > irb(main):018:0> g.call > => 2 > irb(main):019:0> g.call > => 3 > > The closure captures the current scope, i.e. all local variables. > This includes method arguments and "self" - and hence all member > variables of self as well: > > irb(main):023:0> def f; @x = 0; lambda { @x += 1 } end > => nil > irb(main):024:0> g = f > => # > irb(main):025:0> g.call > => 1 > irb(main):026:0> g.call > => 2 > irb(main):027:0> g.call > => 3 > irb(main):028:0> g.call > => 4 > > >> I tried various combinations as well: proc, Proc.new, I think a return >> from method(), calls to a separate object; but there was no impact on the >> result. If "a" isn't cleared, the object is held. > > I would be surprised if there was. lambda and Proc both create a > closure. Difference between lambda and Proc are in a different area: > http://stackoverflow.com/questions/1740046/whats-the-difference-between-a-proc-and-a-lambda-in-ruby > >> I'm guessing that there >> might be some way to say to not touch a thing in the current scope, but I'm >> not sure *how* to specify it. > > There is no way to exclude that variable from the closure - other than > not passing it. But that would be pointless here. :-) > >> I also adapted the main program based on my experience with the code below, >> and suddenly the finalizers were called. So it's the same type of problem. >> >> So there's a problem, and it's avoidable. I know the "what", but don't know >> the "why". There is some subtlety I'm missing. Most interesting. :) > > Now you should know. > > Kind regards > > robert > >