Accessing instance vars in a block?

I’m using rmagick:

img.write(self.path) { self.quality = @quality }

@quality is nil inside the block. How can I access its value inside the
block? I saw there’s apparently a “variable” keyword, but that causes an
error:

img.write(self.path) {
variable @quality
self.quality = @quality
}

Thanks,
Joe

This works but…yuck!

$q = @quality

img.write(self.path) { self.quality = $q }

Joe

On Nov 4, 2006, at 11:24 AM, Joe R. MUDCRAP-CE wrote:

img.write(self.path) {
variable @quality
self.quality = @quality
}

Thanks,
Joe

It looks like maybe ImageMagick is using instance_eval inside that

block. This is why the @instance vars don’t work inside of there. The
ugly workaround is to use local variables:

quality = @quality
img.write(self.path) { self.quality = quality }

Cheers

– Ezra Z.
– Lead Rails Evangelist
[email protected]
– Engine Y., Serious Rails Hosting
– (866) 518-YARD (9273)

Ezra Z. wrote:

block. This is why the @instance vars don’t work inside of there. The
ugly workaround is to use local variables:

quality = @quality
img.write(self.path) { self.quality = quality }

Hmm. This might have been brought up before, but blocks seem rather
underpowered with respects to manipulating them? A way to prevent their
binding scope from being clobbered by using them in an instance_eval
context would be nifty. Or having nested block bindings depending on use

  • so if this block got instance_eval’ed twice inside RMagick, it would
    look up read variable references in all the scopes that apply, last
    object first. With the interpreter looking for and reporting possible
    conflicts as a warning when those are allowed (not otherwise, that
    sounds like a horrid performance hit without any sort of code path
    inference to optimize the detection process.) Of course, this might
    still be a problem with autovivification of instance variables

Of course, this would probably do Cruel and Unusual Things ™ to block
performance, and it’s possible to mitigate the problem by always
documenting what methods rescope their block argument, and then users
actually reading the documentation.
(http://www.simplesystems.org/RMagick/doc/image3.html#write does state
the block sets attributes on another object.)

Of course, that necessitates still the Javascripty hack of reassigning
stuff to instance variables - personally I think instance_eval’ing an
external block is a hideous practice that violates encapsulation with
gay abandon, and therefore shouldn’t -ever- be used across the library /
client boundary. In this case, I’d go the way of yielding the
Image::Info object to the block, even if that robs you of the novel and
exciting feeling of having direct access to a library’s unmentionables
as the common case instead of the special one.

David V.

Joe R. MUDCRAP-CE wrote:

This works but…yuck!
$q = @quality
img.write(self.path) { self.quality = $q }

No reason to use a global. Use a local variable as Ezra suggested.

[email protected] wrote:

how the method works. I don’t think it would be good to try to figure
out where the block came from and set up some mechanism that way.
That goes against the grain of having first-class closures at all.

My point is that with instance_eval, the block doesn’t close over its
lexical scope as is most often expected, but over the instance scope of
another object. At least it seems that way to me. It might be useful,
but it’s an inconsistency that a library can force on its client. The
whole proposal at first was a longshot, and sort of a workaround against
flaky API design.

I think one just has to hope people won’t do the “stealth”
instance_eval. If they do, you’d presumably know about it from the
docs, and can avoid it.

Which is why I’d prefer that libraries don’t do this at all in their
API. It seems like a code / design smell to me to make part of your
object internals part of your API, and there are less iffy ways of
exposing part of your object conditionally - e.g. dynamically defining
extra singleton accessors for the attributes you want the block to have
access to before yielding the object to the block as a regular parameter
and undefining them afterwards. And in the RMagick example, I have
slight doubts that for any and all practical reasons and purposes,
having the Image::Info object as a block parameter to Image#write
instead of using instance_eval would be any different except that it
would surprise users less.

David V.

Hi –

On Sun, 5 Nov 2006, David V. wrote:

Hmm. This might have been brought up before, but blocks seem rather
underpowered with respects to manipulating them? A way to prevent their
binding scope from being clobbered by using them in an instance_eval
context would be nifty.

But instance_eval takes a block; it’s not a special context, but just
how the method works. I don’t think it would be good to try to figure
out where the block came from and set up some mechanism that way.
That goes against the grain of having first-class closures at all.

I think one just has to hope people won’t do the “stealth”
instance_eval. If they do, you’d presumably know about it from the
docs, and can avoid it.

David

Wilson B. wrote:

actually reading the documentation.

This is basically just a Ruby implementation problem. I’m reading
through the Smalltalk ‘Blue Book’ now, and it shows that it is
possible to have blocks that are ‘real’ objects while still offering
very high performance.
This is something we can fix with hard work on the Ruby internals.

Actually, not really. Smalltalk didn’t have eval. The very prospect that
a Ruby block could call eval on any arbitrary text means you can’t, in
the general case, discard a block’s binding (the biggest benefit to ST
block performance was saving yourself closing over the lexical scope
whenever possible) - and either way eval is a general nuisance to
optimise without Dark Magic. In Ruby, IIRC, the performance hit of
constructing the equivalent of a ST full block is worked around by the
duality of “real” and “fake” blocks - the fake ones don’t need being
constructed as objects because their lexical scope is still on the
interpreter stack. Coincidentally, since “real” blocks are always full
blocks in the ST sense of the term, they’re also a yummy inherent memory
leak.

(Someone with more intimate knowledge on Ruby internals might correct me
on the above.)

It’s probably possible to apply some heuristics to try to see if this
might happen as an optimization, but Ruby is a far more complex language
than ST (alias, dynamic requires of source files, eval, constant lookup
to name a few features Blue Book ST didn’t have), and reasoning about
behaviour of Ruby code algorithmically is much harder. So, while it is
probably possible, I wouldn’t expect this to happen anytime soon - it’s
a price you pay for the flexibility and metaprogramming convenience.

Personally, I’d be in favour of either being able to optionally disable
eval in the interpreter if that is in fact the language feature that
makes fast clean blocks impossible (it might be only partially guilty)
and making the standard library eval-free to comply with that.
Alternately, introducing keywords / methods to let a programmer manually
declare which blocks do not reference enclosing locals or the enclosing
object to hand-optimize could work also. (This should be done on
creation, discarding a binding -after- the object is created wouldn’t do
anything for performance, even if it would solve the memory leak
issues.)

David V.

On 11/5/06, David V. [email protected] wrote:

Hmm. This might have been brought up before, but blocks seem rather
underpowered with respects to manipulating them?

Of course, this would probably do Cruel and Unusual Things ™ to block
performance, and it’s possible to mitigate the problem by always
documenting what methods rescope their block argument, and then users
actually reading the documentation.

This is basically just a Ruby implementation problem. I’m reading
through the Smalltalk ‘Blue Book’ now, and it shows that it is
possible to have blocks that are ‘real’ objects while still offering
very high performance.
This is something we can fix with hard work on the Ruby internals.

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs