[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Accessing instance vars in a block?

Joe Ruby

11/4/2006 7:24:00 PM

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

--
Posted via http://www.ruby-....

8 Answers

Ezra Zygmuntowicz

11/4/2006 7:27:00 PM

0


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

> 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


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 Zygmuntowicz
-- Lead Rails Evangelist
-- ez@engineyard.com
-- Engine Yard, Serious Rails Hosting
-- (866) 518-YARD (9273)



Joe Ruby

11/4/2006 7:50:00 PM

0

This works but...yuck!

$q = @quality

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


Joe

--
Posted via http://www.ruby-....

Gavin Kistner

11/4/2006 11:54:00 PM

0

Joe Ruby 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.

David Vallner

11/5/2006 12:18:00 PM

0

Ezra Zygmuntowicz wrote:
>
> On Nov 4, 2006, at 11:24 AM, Joe Ruby MUDCRAP-CE wrote:
>
>> I'm using rmagick:
>>
>> img.write(self.path) { self.quality = @quality }
>>
>> @quality is nil inside the block.
>
> 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 }
>

<mad-rant>

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

</mad-rant>

Of course, this would probably do Cruel and Unusual Things (tm) 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.... 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 Vallner

dblack

11/5/2006 1:20:00 PM

0

David Vallner

11/5/2006 4:01:00 PM

0

dblack@wobblini.net wrote:
> Hi --
>
> On Sun, 5 Nov 2006, David Vallner 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.
>

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 Vallner

Wilson Bilkovich

11/5/2006 11:54:00 PM

0

On 11/5/06, David Vallner <david@vallner.net> wrote:
> <mad-rant>
>
> Hmm. This might have been brought up before, but blocks seem rather
> underpowered with respects to manipulating them?
> </mad-rant>
>
> Of course, this would probably do Cruel and Unusual Things (tm) 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.

David Vallner

11/6/2006 12:27:00 AM

0

Wilson Bilkovich wrote:
> On 11/5/06, David Vallner <david@vallner.net> wrote:
>> <mad-rant>
>>
>> Hmm. This might have been brought up before, but blocks seem rather
>> underpowered with respects to manipulating them?
>> </mad-rant>
>>
>> Of course, this would probably do Cruel and Unusual Things (tm) 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.
>

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 Vallner