Local variables & blocks

Block parameters are only way to create block local variables, but
they still exists. If I read right, there's no block local variables
including block parameters in your proposal, right?

Right.

Clearly there is some magical distinction between block parameters and
'ordinary' variables, bound up somehow in closures - something which I've
never had a requirement for in real life.

I'll need to take some time to get my head around it :slight_smile:

I think I've at least made a start now.

One thing I've realised is that Proc objects must have their own local
variables - for their parameters, at least - in case they call themselves
recursively.

I can't see how to get a Proc object to refer to itself except by passing a
reference to itself as one of its arguments, and I have not yet reached
sufficient enlightenment to know when you'd actually want to do this, but
here's a contrived example:

···

On Mon, Feb 03, 2003 at 02:23:51AM +0900, Brian Candler wrote:

> Block parameters are only way to create block local variables, but
> they still exists. If I read right, there's no block local variables
> including block parameters in your proposal, right?

Right.

Clearly there is some magical distinction between block parameters and
'ordinary' variables, bound up somehow in closures - something which I've
never had a requirement for in real life.

I'll need to take some time to get my head around it :slight_smile:

-------------------------------------------------------------------------
def recursor(op)
  return proc do |me,acc,count|
    if count <= 0
      acc
    else
      me.call(me, acc.send(op,count), count-1)
    end
  end
end

summer = recursor("+")
puts summer.call(summer,0,10) # 10+9+8+...

factorial = recursor("*")
puts factorial.call(factorial,1,8) # 8*7*6*...
-------------------------------------------------------------------------

In the function 'recursor', 'op' is a local variable scoped to the function,
and me/acc/count are local variables scoped to the do..end block only, which
get separate values each time the function invokes itself.

The variable 'op' is bound to a value when 'recursor' is called, and remains
bound to it: e.g. summer references a Proc object with op bound to "+"

'op' is only used in a read-only manner in the above example, but the
current Ruby implementation does not stop it from changing it - in which
case the behaviour of the Proc object could change on each call. The bound
variables behave like hidden instance variables.

So if you want to be perverse:

-------------------------------------------------------------------------
def recursor(op)
  return proc do |me,acc,count|
    if count <= 0
      if op=='+' then op='-' # swap the op
      elsif op=='-' then op='+'
      end
      acc
    else
      me.call(me, acc.send(op,count), count-1)
    end
  end
end

summer = recursor("+")
puts summer.call(summer,0,10) >> 55
puts summer.call(summer,0,10) >> -55
-------------------------------------------------------------------------

Now, I am no computer scientist, but I'd like to know: is it generally
considered that closures aren't supposed to carry state in this way? That's
what objects and instance variables are for. Or is there a valid reason for
the closure to be able to alter the environment it was created in?

Something which the example above _doesn't_ highlight is the use of other
variables defined locally to the block. Suppose I need to create some
intermediate value 'i' while calculating: in general we'll need a fresh copy
of 'i' for each recursive invocation. And in Ruby, if we accidentally
already had a local variable called 'i' outside the block, then there will
be a single value for 'i' shared between each iteration, and it will break.

To make this example even more contrived:

-------------------------------------------------------------------------
def recursor(op)
  #i=0
  return proc do |me,acc,count|
    if count <= 0
      i = 10
      acc
    else
      i = 0
      me.call(me, acc.send(op,count), count-1) + i
    end
  end
end

summer = recursor("+")
puts summer.call(summer,0,10)
-------------------------------------------------------------------------

This works, but if you uncomment the 'i=0' outside the block, it goes
wrong, because you end up with a single value of 'i' shared between all the
iterations.

I think this is what makes me uncomfortable with Ruby at the moment: "i=0"
might create either a block local variable (separate for each recursion into
the function, or separate for each thread when we're talking about threads)
- or it might be a static variable shared by them all - and the syntax is
the same in both cases.

One of the fundamentals of Ruby is that there are no declarations. Well, I
don't think that's strictly true: "foo = 0" is a declaration, whether you
like it or not, because it's an instruction to the compiler to treat
subsequent uses of bareword "foo" as a local variable, rather than a method
call to self.foo. But I can cope with one level of implicit declaration;
it's the other levels of implicit declaration which I find unappealing.

It seems to me that if you want to avoid declarations, implicit or explicit,
then you need to eliminate the distinction which the declaration is
expressing.

Given that we must have block-local variables at least for block parameters,
and ideally also for intermediate values calculated within a block, then
here is where my thought train is leading: why not make *everything*
block-local? By that, I mean that *all* the local variables, both inside and
outside the block, get a fresh copy each time the block is invoked (and thus
are restored when the block terminates), all held in the same frame.

This appears to solve a number of problems at a stroke. All variables can be
used recursively, whether or not they were previously used before the block;
and all variables are thread-local.

It would mean that closures couldn't alter their external environment as a
way of carrying state, as described earlier. Unfortunately, it would
probably also break the ability for code within a block to set a value which
persists afterwards:

  found = false
  mylist.each do { .... found = true if <whatever> ... }
  found and puts "Got it"

and that is, of course, a very useful pattern.

If it were possible to duplicate the local stack frame only when the proc is
*re*invoked recursively, we might have a solution... that's more than enough
rambling though :slight_smile:

Regards,

Brian.

That’s exactly how its done. The trick is to removed the “me” variable
from the argument list by embedding the recursive function inside
another proc that binds “me”. Something like this …

proc do |me|
proc do |acc,count|
#…
end
end

Then all you have to get “me” bound to the right proc instance.

The result is call a Y combinator, for reasons I don’t fully
understand. I’m always at the brink of understanding the Y combinator,
but never fully “get it”. It is fun to play with however.

Here’s a ruby version of Y.
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/20469

And for those who really wish to torture themselves, here it is in Java.
http://www.clug.org//ml/archive/programming/1998-11/msg00014.html

And if you are interested in the machanics of Y, here it is again in
scheme (where I first learned it) along with some discussion on how to
build it …
http://www.clug.org//ml/archive/programming/1998-11/msg00028.html

As a final thought along the recreational uses of “proc”, I leave you
with the following code inspired by the thread on block and local
variables.

def weird
x = nil
[proc {x}, proc {|x|}]
end

What does it do? Hint: This works in the current version of Ruby, but
it will fail when/if Matz ever implements the new rules for blocks and
local scope.

···

On Mon, 2003-02-03 at 17:46, Brian Candler wrote:

I can’t see how to get a Proc object to refer to itself except by passing a
reference to itself as one of its arguments,

–
– Jim Weirich jweirich@one.net http://w3.one.net/~jweirich

“Beware of bugs in the above code; I have only proved it correct,
not tried it.” – Donald Knuth (in a memo to Peter van Emde Boas)

Hi Jim,

thanks for the weird Y fun :slight_smile:

Isn’t your weird the same as

def weird
x = nil
[proc {x}, proc {|v| x=v}]
end

and wouldn’t this work under the new rules, too?

Regards,
Pit

···

On 4 Feb 2003 at 14:27, Jim Weirich wrote:

(… Y combinator …)

As a final thought along the recreational uses of “proc”, I leave you
with the following code inspired by the thread on block and local
variables.

def weird
x = nil
[proc {x}, proc {|x|}]
end

What does it do? Hint: This works in the current version of Ruby, but
it will fail when/if Matz ever implements the new rules for blocks and
local scope.

Yes, it is the same. While thinking about the proposed rules for block
and local scope, I was just struck by the weird symmetry between {x} and
{|x|}.

And while we have mentioned the new scoping rules, I think the new
rules are fine. They are simplier and more regular than the existing
rules. Its a little odd to declare block local varaibles using the
“local” trick, but then it kinda neat that the language can handle that
as well without any special rules.

···

On Tue, 2003-02-04 at 05:01, Pit Capitain wrote:

Hi Jim,

thanks for the weird Y fun :slight_smile:

Isn’t your weird the same as

def weird
x = nil
[proc {x}, proc {|v| x=v}]
end

and wouldn’t this work under the new rules, too?

–
– Jim Weirich jweirich@one.net http://w3.one.net/~jweirich

“Beware of bugs in the above code; I have only proved it correct,
not tried it.” – Donald Knuth (in a memo to Peter van Emde Boas)

Hi Jim,

thanks for the weird Y fun :slight_smile:

Isn’t your weird the same as

def weird
x = nil
[proc {x}, proc {|v| x=v}]
end

and wouldn’t this work under the new rules, too?

Yes, it is the same. While thinking about the proposed rules for block
and local scope, I was just struck by the weird symmetry between {x} and
{|x|}.

And while we have mentioned the new scoping rules, I think the new
rules are fine. They are simplier and more regular than the existing
rules. Its a little odd to declare block local varaibles using the
“local” trick, but then it kinda neat that the language can handle that
as well without any special rules.

–
– Jim Weirich jweirich@one.net http://w3.one.net/~jweirich

“Beware of bugs in the above code; I have only proved it correct,
not tried it.” – Donald Knuth (in a memo to Peter van Emde Boas)

···

----- Original Message -----
From: “Jim Weirich” jweirich@one.net
To: “ruby-talk ML” ruby-talk@ruby-lang.org
Sent: Tuesday, February 04, 2003 9:39 PM
Subject: Re: Local variables & blocks

On Tue, 2003-02-04 at 05:01, Pit Capitain wrote: