From: ptkwt@... (Phil Tomson) Date: 2005-03-29T16:49:49+09:00 Subject: Re: User defined loops (applicable to DSLs) In article , Phil Tomson wrote: >In article , >Relm wrote: >>On Mon, 28 Mar 2005, Phil Tomson wrote: >> >>> Actually, I figured out a way to make it much more natural: >>> >>> Change Whiler to accept an optional condition in it's constructor like >>> so: >>> >>> class Whiler >>> def initialize(cond=nil) >>> @ret = nil >>> @cond = cond >>> end >>> def while(cond=@cond) >>> break @ret unless cond >>> @ret = yield >>> retry >>> end >>> end >>> >>> >>> Define the my_while top-level method like so: >>> >>> def my_while(cond,&block) >>> Whiler.new(cond).method(:while).call(&block) >>> end >> >>This new my_while doesn't seem to return the last block. The following >>snippet outputs nil values instead of "outer" and "inner": >> >> i = 0 >> x = nil >> y = nil >> >> x = my_while(i<10) do >> j=0 >> y = my_while(j<10) do >> j+=1 >> "inner" >> end >> i+=1 >> "outer" >> end >> >> puts x >> puts y >> > >Hmmm... I'm seeing the same thing you are, but I'm not sure why. >Anyone got any ideas? Ah, I see the problem now. my_while calls Whiler.new every time it is called of course, thus creating a new Whiler object each time. The last time through each loop, the condition is false when Whiler.new is called and thus @ret is nil because my_while returns before getting to the '@ret = yield'. Definately problematic. You can see the problem by adding a 'puts' to the Whiler constructor like so: class Whiler def initialize(cond=nil) puts "Whiler::new(#{cond})" @ret = nil @cond = cond end end >>This makes your original my_while thread-safe. It was already safe for >>nesting. >> >> def my_while(cond) >> @ret ||= {} >> key = Thread.current >> return @ret.delete(key) unless cond >> @ret[key] = yield >> retry >> end >> > This is probably the best bet for being both threadsafe and nestable. Phil