On 8/1/06, Chad Perrin <perrin / apotheon.com> wrote:
> Specifically, it sounds like what you're saying here would indicate that
> this is a closure:
>
>   def foo
>     bar = 0
>     lambda { puts bar += 1 }
>   end
>
>   baz = foo
>
> and this is a closure:
>
>   def foo
>     lambda { puts "Hellow world!" }
>   end
>
>   bar = foo

Yes, this is exactly the point we've been trying to get across. Both
of these are closures.

> but this is not a closure:
>
>   foo = 0
>
>   foo.each do
>     puts bar += 1
>   end

This is where we get into what David Black described as hair
splitting. Lets move away from each, and to a set of more general
methods:

  greeting = "Hello"

  # 1) explicit proc
  def foo(callback)
    callback.call("world")
  end

  callback = lambda{ |arg| puts "#{greeting}, #{arg}!" }
  foo(callback)
  # prints "Hello, world!"

  # 2) block converted to proc
  def bar(&callback)
    callback.call("world")
  end

  bar{ |arg| puts "#{greeting}, #{arg}!" }
  # prints "Hello, world!"

  # 3) block yielded to
  def baz
    yield "world"
  end

  baz{ |arg| puts "#{greeting}, #{arg}!" }
  # prints "Hello, world!"

The first example obviously uses a closure. While true that the
environment where "greeting" is defined never goes out of scope, it is
not visible within the context where callback is actually executed.

The second example performs the exact same operation as the first, the
only difference is the syntax and *when* the chunk of code becomes a
proc.

The third example simply removes the explicit conversion from a block
to a proc, using yield instead. As such, the semantics between all
three are similar and example three can be arguably said to use a
closure as well. This is the stance Gennady and "the Victim" are
taking.

David's point, however, is that in the third example, when yield is
used instead of converting it to a block, the code inside the block
never really leaves the context in which it is created, because of the
way non-converted blocks are implemented. More specifically, that
implementation doesn't need to store that environment. This is a
probable reason for the speed difference between converted blocks
(first class procs) and unconverted blocks (yield). Since the
environment is never stored, it's not really a closure *in the
implementation*. This is the hair that David was splitting.

But semantically, whether a block is converted to a proc or just
yielded to, the behavior regarding variable scope is identical; so if
one is a closure, it's useful to refer to the other as a closure as
well, even if it's not implemented as a true closure. In this sense,
all blocks are closures.

Jacob Fugal