This thread caused me to do some experimenting and I came across some
Proc semantics that I hadn't seen discussed before and that might
be useful in a DSL context. Consider:
class A
Foo = define_method(:foo) { |x|
p "self: #{self}"
return 'positive' if x > 0
'negative'
}
end
p A::Foo # #<Proc:0x00026520 / proc.question:6>
p A::Foo.call(0) # "self: A" "negative"
p A::Foo.call(1) # "self: A" "positive"
p A.new.foo(0) # "self: #<A:0x25ecc>" "negative"
p A.new.foo(1) # "self: #<A:0x25e2c>" "positive"
I was unaware that define_method returned a proc. I was even more
surprised that it appears to be of the lambda "flavor". That is to
say that a 'return' within the block is relative to the block and not
to the scope where the block is defined.
Previously I thought that only Kernel#proc and Kernel#lambda had
the 'magic' property of altering the return semantics of a block.
I understand why this is also necessary for define_method to be
useful, I was just not aware that the resulting proc was accessible
as the return value of define_method.
I was going to ask if there was any way to write a method like
'define_method' that forces any provided blocks to be interpreted
as lambda vs. 'regular' procs. Then I did some more experimenting:
a = Proc.new { return 42 }
a.call # LocalJumpError
b = lambda &a
a.call # 42
This was another surprise for me as I figured that when lambda was
used, the first proc would be 'wrapped' in a new proc with
lambda semantics, something like:
c = lambda { a.call }
But that isn't the case. If you try 'c.call' you'll get the
LocalJumpError again.
I hadn't see this type of transformation before. It might be
useful in some DSL situations:
class A
filter {|x| x.to_s } # ok
filter {|x| return x.to_s } # potential LocalJumpError
end
If you wrote filter as:
def A.filter(&b)
lambda(&b).call(42)
end
you can avoid the LocalJumpError.
Gary Wright