From: Ezra Zygmuntowicz Date: 2006-10-19T11:38:53+09:00 Subject: Re: whats this lambda code doing? On Oct 18, 2006, at 6:56 PM, Morton Goldberg wrote: > On Oct 18, 2006, at 9:12 PM, Ezra Zygmuntowicz wrote: > >> >> On Oct 18, 2006, at 6:02 PM, David Vallner wrote: >> >>> Ezra Zygmuntowicz wrote: >>>> klasses.inject(block) do |blk, klass| >>>> lambda { klass.with_scope(scope_hash, &blk) } >>>> end.call >>>> >>>> That outer block that ends on end returns the lambda made inside >>>> of that >>>> block. So end.call ends up being lambda { klass.with_scope >>>> (scope_hash, >>>> &blk) }.call >>>> >>> >>> Hmm. Unless I'm very mistaken, it would also only call the lambda >>> for >>> the last object in klasses, generating several garbage (and >>> relatively >>> expensive) lambdas. Boggle. Am I missing something? >>> >>> David Vallner >> >> It looks like it would only call the last lambda but it does call >> all of them. Here is a simplification of whats happening. >> >> irb(main):065:0> def scoper(klasses=[:foo, :bar, :baz], &block) >> irb(main):066:1> klasses.inject(block) do |blk, klass| >> irb(main):067:2* lambda { puts klass; blk.call} >> irb(main):068:2> end.call >> irb(main):069:1> end >> => nil >> irb(main):070:0* scoper { puts '&block called' } >> baz >> bar >> foo >> &block called >> => nil >> irb(main):071:0> > > I think I'm still confused by this. I infer from your post that the > lambdas get nested by the inject (at the position of 'blk') with > the first one innermost, which is why they get called in reverse > order (and 'block' last of all). Is that right? > > Regards, Morton > Yes thats essentially it. Maybe this helps clear it up a bit. Lets forget about the with_scope AR stuff for a minute and just mock this out: class Scoper def self.with_block(hsh={}, &block) p hsh.merge( {:klass => name}) block.call end end class Foo < Scoper end class Bar < Scoper end class Baz < Scoper end def set_scopers(klasses=[Foo, Bar, Baz], &block) hash = {:test => 'test'} klasses.inject(block) do |blk, klass| lambda { klass.with_block hash, &blk } end.call end puts set_scopers { puts 'done'} # outputs {:test=>"test", :klass=>"Baz"} {:test=>"test", :klass=>"Bar"} {:test=>"test", :klass=>"Foo"} done Now if you comment out the block.call line in the Scoper.with_block method: class Scoper def self.with_block(hsh={}, &block) p hsh.merge( {:klass => name}) #block.call end end puts set_scopers { puts 'done' } #ouput {:test=>"test", :klass=>"Baz"} nil and run the same code again it only calls the last lambda that gets made. It is a twisty little mess of block and wrappers that is hard to follow. But essentially inject is chaning those lambda's together and the final .call calls the chain which calls the final &blk in the end. nil Cheers- -- Ezra Zygmuntowicz -- Lead Rails Evangelist -- ez@engineyard.com -- Engine Yard, Serious Rails Hosting -- (866) 518-YARD (9273)