Block Scope Statement in the Pickaxe

Hi,

I stumbled across this quote in chapter 15, “Ruby Tk”, which confused me a
bit:

“One small caution when using the code block form: the scope of variables is
not what you think it is. The block is actually evaluated in the context of
the widget’s object, not the caller’s. This means that the caller’s instance
variables will not be available in the block, but local variables from the
enclosing scope and globals (not that you ever use those) will be.”

How can this be? I mean, AFAIK the block’s binding refers to client code
and cannot be changed afterwards. Or did I miss something? Is this some
kind of implementation trick that can be done with a Ruby extension?

Thanks for an explanation.

Regards

robert

Robert Klemme wrote:

I stumbled across this quote in chapter 15, “Ruby Tk”, which confused me a
bit:

“One small caution when using the code block form: the scope of variables is
not what you think it is. The block is actually evaluated in the context of
the widget’s object, not the caller’s. This means that the caller’s instance
variables will not be available in the block, but local variables from the
enclosing scope and globals (not that you ever use those) will be.”

How can this be? I mean, AFAIK the block’s binding refers to client code
and cannot be changed afterwards. Or did I miss something? Is this some
kind of implementation trick that can be done with a Ruby extension?

Looking at the Tk source code, it appears atleast it uses a Ruby
extension to do so.
In tkutil.c a toplevel definition of ‘new’ which sends any block into
instance_eval without having to deal with the shackles that our
pure-Ruby instance_eval enforces (no Procs).

It doesn’t appear to do much else and it is written by Matz himself so
if there was an elegant pure Ruby way, it would probably use it. (The
file is dated 2001, though :slight_smile:

As for how all this works, my head exploded when I came down into
rb_yield_0 in eval.c, so I’m going to stop trying now… :-S

···


([ Kent Dahl ]/)_ ~ [ http://www.pvv.org/~kentda/ ]/~
))_student_/(( _d L b_/ (pre-) Master of Science in Technology )
( __õ|õ// ) )Industrial economics and technological management(
_
/ö____/ (_engineering.discipline=Computer::Technology)

Robert Klemme wrote:

Hi,

I stumbled across this quote in chapter 15, “Ruby Tk”, which confused me a
bit:

“One small caution when using the code block form: the scope of variables is
not what you think it is. The block is actually evaluated in the context of
the widget’s object, not the caller’s. This means that the caller’s instance
variables will not be available in the block, but local variables from the
enclosing scope and globals (not that you ever use those) will be.”

How can this be? I mean, AFAIK the block’s binding refers to client code
and cannot be changed afterwards. Or did I miss something? Is this some
kind of implementation trick that can be done with a Ruby extension?

I think this is just the distinction between #instance_eval and calling
a block directly. With instance_eval, the binding of self in the block
is changed to the receiver of instance_eval. Hope this example says it
better:

class A
attr_reader :x
def initialize
@x = 1
end
def twiddle(&block)
block.call
end
def frob(&block)
instance_eval(&block)
end
end

a = A.new

some_local_var = 2
@x = 10

a.twiddle do
some_local_var += 1
@x += 1
end

p some_local_var, @x, a.x # => 3, 11, 1

a.frob do
some_local_var += 1
@x += 1
end

p some_local_var, @x, a.x # => 4, 11, 2

“Joel VanderWerf” vjoel@PATH.Berkeley.EDU schrieb im Newsbeitrag
news:3EF7E177.4060808@path.berkeley.edu…

I think this is just the distinction between #instance_eval and calling
a block directly. With instance_eval, the binding of self in the block
is changed to the receiver of instance_eval. Hope this example says it
better:

Thanks! That cleared the fog from my eyes. I thought the *eval family of
methods would only work on strings.

I came up with this condensed example:

class Foo
def foo; yield; end
def bar(&b); instance_eval(&b); end
end

f = Foo.new

f.foo {p self} # → main (in IRC)
f.bar {p self} # → <Foo…

Cheers

robert

Kent Dahl wrote:

Looking at the Tk source code, it appears atleast it uses a Ruby
extension to do so.
In tkutil.c a toplevel definition of ‘new’ which sends any block into
instance_eval without having to deal with the shackles that our
pure-Ruby instance_eval enforces (no Procs).

Ok, the rest of my message turns out to be hogwash, since I got confused
by the combination by a & missing on my part and the following error
message:
in `instance_eval’: wrong argument type Proc (expected String)
(TypeError)

Now this might just be me being an idiot (or sleepy), but I’ll try to
voice a CFD on a potential RCR.

Since instance_eval may take a block, would it make sense to change the
above error message to something like:
in `instance_eval’: wrong argument type Proc (expected String or block)
(TypeError)
for those one-character typing errors? This would also fit the rest of
the *_eval family of methods that take blocks.

Pro:

  • The interface to the method is more clearly expressed in the error
    message.

Con:

  • Eval is evil so any obfuscating helps protect the young ones while
    securing job security for the old ones.
···


([ Kent Dahl ]/)_ ~ [ http://www.pvv.org/~kentda/ ]/~
))_student_/(( _d L b_/ (pre-) Master of Science in Technology )
( __õ|õ// ) )Industrial economics and technological management(
_
/ö____/ (_engineering.discipline=Computer::Technology)

“Kent Dahl” kentda@stud.ntnu.no schrieb im Newsbeitrag
news:3EF8143F.45CFEEDD@stud.ntnu.no…

Since instance_eval may take a block, would it make sense to change the
above error message to something like:
in `instance_eval’: wrong argument type Proc (expected String or block)

IMHO this is not correct. You might call me pricky but a block can never
be a method parameter. You can either have a block associated with a
method invocation OR have a proc as parameter. Of course an associated
block can be easily converted into a proc for subsequent invocations. I’d
rather change it to

in `instance_eval’: wrong argument type Proc (expected String or no args
and associated block)

Cheers

Robert

Robert Klemme wrote:

“Kent Dahl” kentda@stud.ntnu.no schrieb im Newsbeitrag
news:3EF8143F.45CFEEDD@stud.ntnu.no…

Since instance_eval may take a block, would it make sense to change the
above error message to something like:
in `instance_eval’: wrong argument type Proc (expected String or block)

IMHO this is not correct. You might call me pricky but a block can never
be a method parameter. You can either have a block associated with a
method invocation OR have a proc as parameter. Of course an associated
block can be easily converted into a proc for subsequent invocations. I’d
rather change it to

in `instance_eval’: wrong argument type Proc (expected String or no args
and associated block)

Point taken. For brevity I think I would prefer simply:
in `instance_eval’: wrong argument type Proc (expected String or
associated block)

The ‘no args’ bits feels like line noise reading it.

···


([ Kent Dahl ]/)_ ~ [ Kent Dahl - Kent Dahl ]/~
))_student_/(( _d L b_/ (pre-) Master of Science in Technology )
( __õ|õ// ) )Industrial economics and technological management(
_
/ö____/ (_engineering.discipline=Computer::Technology)

“Kent Dahl” kentda@stud.ntnu.no schrieb im Newsbeitrag
news:3EF829B4.80051432@stud.ntnu.no…

Robert Klemme wrote:

“Kent Dahl” kentda@stud.ntnu.no schrieb im Newsbeitrag
news:3EF8143F.45CFEEDD@stud.ntnu.no…

Since instance_eval may take a block, would it make sense to change
the
above error message to something like:
in `instance_eval’: wrong argument type Proc (expected String or
block)

IMHO this is not correct. You might call me pricky but a block can
never
be a method parameter. You can either have a block associated with a
method invocation OR have a proc as parameter. Of course an
associated
block can be easily converted into a proc for subsequent invocations.
I’d
rather change it to

in `instance_eval’: wrong argument type Proc (expected String or no
args
and associated block)

Point taken. For brevity I think I would prefer simply:
in `instance_eval’: wrong argument type Proc (expected String or
associated block)

The ‘no args’ bits feels like line noise reading it.

Sounds good. Now that that’s settled, who’s gonna put it into source
code? :slight_smile:

robert

Robert Klemme wrote:

“Kent Dahl” kentda@stud.ntnu.no schrieb im Newsbeitrag

Point taken. For brevity I think I would prefer simply:
in `instance_eval’: wrong argument type Proc (expected String or
associated block)

The ‘no args’ bits feels like line noise reading it.

Sounds good. Now that that’s settled, who’s gonna put it into source
code? :slight_smile:

As I went looking into the source, I noticed that
the behaviour has changed from 1.6.8 to 1.8.0-pre3:

[kentda@v052a ruby-1.8.0]$ ./ruby -v
ruby 1.8.0 (2003-06-23) [i686-linux]
[kentda@v052a ruby-1.8.0]$ ./ruby -e “instance_eval proc { }”
-e:1:in `instance_eval’: cannot convert Proc into String (TypeError)
from -e:1

The reason appears to be due to a more generic approach where any object
pretending to be a string (i.e. implements to_str) is acceptable.

I can’t see any elegant way to do it. Either:

  1. Check if it is a Proc before the to_str stuff. But what if it the
    Proc has to_str implemented?
  2. Catch the exception and augment the error message.

Hmm, maybe it is too much hand-holding to do so. I’m leaning back
towards the “I was sleepy/an idiot at the time” position.

···


([ Kent Dahl ]/)_ ~ [ Kent Dahl - Kent Dahl ]/~
))_student_/(( _d L b_/ (pre-) Master of Science in Technology )
( __õ|õ// ) )Industrial economics and technological management(
_
/ö____/ (_engineering.discipline=Computer::Technology)