From: gwtmp01@... Date: 2005-06-11T12:11:09+09:00 Subject: Re: blocks, scope/context confusion On Jun 10, 2005, at 9:55 PM, Corey wrote: > I'm reading pages 358 & 359 in the pickaxe2 and having a tough time > wrapping my mind around what exactly is going on. I'm still climbing up the Ruby learning curve so I hope someone will gently correct my answers if I get them wrong :-) In all these examples and my descriptions below I'm talking about what the book calls "raw procs" as opposed to "lambdas" which have slightly different semantics. > What's the difference between the two methods below? > > def meth1 > yield > end > > def meth2(&b) > b.call > end There is no semantic difference. The first example is syntactic sugar for the second example. I believe there is a small "implementation" difference though. In the first example Ruby never converts the block associated with a call to meth1 to a full-blown instance of Proc. In the second example, the block is packaged up into a Proc object and then the method lookup for "call" results in the execution of the block. So the first example saves Ruby the trouble of allocating an object and then dispatching the method, and then the garbage collector eventually having to reclaim the discarded Proc. > What's the difference between the next two blocks? Does 'next' in > any way > make any sort of functional or subtle difference? > > blk = meth1 { 99 } > puts blk > > blk = meth1 { next 99 } > puts blk Also no semantic difference but that is just because the next statement happens to be the last expression in the block (from both a syntax and execution standpoint). Another way to say this is that the result of execution falling off the end of a block is for ruby to execute an implicit next val where 'val' is the value of the last expression in the block. > Why does the following cause an exception? > > meth { return 99 } Because in this particular example, the block is defined in the top-level context and at the top-level there is no active method and so there is nothing to "return" from. You get the same error if your entire ruby program was just: return 99 When the block is defined within a method context you don't get the error: def foo meth1 { return 99 } return "impossible" end foo # 99 Note that the 'return "impossible"' statement is never executed. > "A return from a block whose original context is not longer valid > raises > an exception ( LocalJumpError )." > In the following: > > meth { return 99 } > > _Where_/_What_ exactly _is_ the "original context", and how exactly > does > it become "no longer valid" with the 'return' statement? I think it is a bad example because the original context was *never* valid to start with. Here is a different example where the context exists and is valid at the time the block is defined but doesn't exist (and so is no longer valid) at the time the block is executed: def level1 level2 { return "return from level1" } end def level2(&block) return block end bogus_block = level1 bogus_block.call # ERROR Lambdas: The main difference with a block that is converted into a lambda is that a 'return' statement in a lambda is associated with the lambda itself and not the method that was active at the time associated block was defined. So within a lambda, 'return' has the same semantics as 'next' as described above. def level1 level2 { return "return from lambda" } end def level2(&block) return (lambda &block) end valid_lambda = level1 valid_lambda.call # "return from lambda" Gary Wright