Accessing instance vars in a block?

  • Thread starter Joe Ruby MUDCRAP-CE
  • Start date
J

Joe Ruby MUDCRAP-CE

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
 
E

Ezra Zygmuntowicz

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
-- (e-mail address removed)
-- Engine Yard, Serious Rails Hosting
-- (866) 518-YARD (9273)
 
J

Joe Ruby MUDCRAP-CE

This works but...yuck!

$q = @quality

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


Joe
 
P

Phrogz

Joe said:
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.
 
D

David Vallner

--------------enigAD0F3593FE5FC3ACA2DF3B9D
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable

Ezra said:
=20
On Nov 4, 2006, at 11:24 AM, Joe Ruby MUDCRAP-CE wrote:
=20
=20
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:
=20
quality =3D @quality
img.write(self.path) { self.quality =3D quality }
=20

<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.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 Vallner


--------------enigAD0F3593FE5FC3ACA2DF3B9D
Content-Type: application/pgp-signature; name="signature.asc"
Content-Description: OpenPGP digital signature
Content-Disposition: attachment; filename="signature.asc"

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.5 (MingW32)

iD8DBQFFTdZ1y6MhrS8astoRApeGAJ92DvTOUOqgRIJUmywwyiFD2ThNVQCbBKIK
iY34wSUwccYjGp73sHWTKcc=
=cXCd
-----END PGP SIGNATURE-----

--------------enigAD0F3593FE5FC3ACA2DF3B9D--
 
D

dblack

Hi --

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

--
David A. Black | (e-mail address removed)
Author of "Ruby for Rails" [1] | Ruby/Rails training & consultancy [3]
DABlog (DAB's Weblog) [2] | Co-director, Ruby Central, Inc. [4]
[1] http://www.manning.com/black | [3] http://www.rubypowerandlight.com
[2] http://dablog.rubypal.com | [4] http://www.rubycentral.org
 
D

David Vallner

--------------enigC945C8CDC6AFA8147C2F82B6
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable

Hi --
=20
On Sun, 5 Nov 2006, David Vallner wrote:
=20
=20
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.
=20

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.
=20

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


--------------enigC945C8CDC6AFA8147C2F82B6
Content-Type: application/pgp-signature; name="signature.asc"
Content-Description: OpenPGP digital signature
Content-Disposition: attachment; filename="signature.asc"

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.5 (MingW32)

iD8DBQFFTgqpy6MhrS8astoRAtgkAJ99bfl9965S/v8mpkDKxbbWxKIbeACdGdMV
MnHYNZ2p3qoJLYusHc+zRWE=
=pbJm
-----END PGP SIGNATURE-----

--------------enigC945C8CDC6AFA8147C2F82B6--
 
W

Wilson Bilkovich

<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.
 
D

David Vallner

--------------enig2E2214425921D50733523A29
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable

Wilson said:
=20
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.
=20

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


--------------enig2E2214425921D50733523A29
Content-Type: application/pgp-signature; name="signature.asc"
Content-Description: OpenPGP digital signature
Content-Disposition: attachment; filename="signature.asc"

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.5 (MingW32)

iD8DBQFFToFiy6MhrS8astoRAkS/AJ9Q1ILAIAj3svM7QxIQU3sOzd5eVACfY9jp
lRwSk5cYWW6wuM3VI3nlJkw=
=bI3G
-----END PGP SIGNATURE-----

--------------enig2E2214425921D50733523A29--
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,764
Messages
2,569,567
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top