Instance Eval of a Proc

G

Gavin Kistner

Why can I not use a Proc created from a method in #instance_eval? (Or
rather, am I making a mistake, or is there a way to do this?)

Related: are first-class functions planned for 2.0? The whole Proc/
block/method situation needs unifying and simplifying, IMHO.


[Slim:~/Desktop] gavinkis% cat instance_eval_proc.rb
class Foo
def self.show_name( *args )
puts "Foo.show_name says that @name is '#@name' for #
{self.inspect}"
end
def initialize( name )
@name = name
end
end

f = Foo.new( "Jim" )

my_lambda = lambda{
puts "my_lambda says that @name is '#@name' for #{self.inspect}"
}
p my_lambda
f.instance_eval( &my_lambda )

my_proc = Proc.new{
puts "my_proc says that @name is '#@name' for #{self.inspect}"
}
p my_proc
f.instance_eval( &my_proc )

meth_lambda = Foo.method( :show_name ).to_proc
p meth_lambda
f.instance_eval( &meth_lambda )


[Slim:~/Desktop] gavinkis% ruby instance_eval_proc.rb
#<Proc:0x001ca42c@instance_eval_proc.rb:12>
my_lambda says that @name is 'Jim' for #<Foo:0x1c9d24 @name="Jim">
#<Proc:0x001ca10c@instance_eval_proc.rb:18>
my_proc says that @name is 'Jim' for #<Foo:0x1c9d24 @name="Jim">
#<Proc:0x001c9bf8@instance_eval_proc.rb:24>
Foo.show_name says that @name is '' for Foo


[Slim:~/Desktop] gavinkis% ruby --version
ruby 1.8.2 (2004-12-25) [powerpc-darwin8.0.0]


[Slim:~/Desktop] gavinkis% uname -a
Darwin Slim.local 8.2.0 Darwin Kernel Version 8.2.0: Fri Jun 24
17:46:54 PDT 2005; root:xnu-792.2.4.obj~3/RELEASE_PPC Power Macintosh
powerpc
 
A

Ara.T.Howard

Why can I not use a Proc created from a method in #instance_eval? (Or rather,
am I making a mistake, or is there a way to do this?)

Related: are first-class functions planned for 2.0? The whole Proc/
block/method situation needs unifying and simplifying, IMHO.


[Slim:~/Desktop] gavinkis% cat instance_eval_proc.rb
class Foo
def self.show_name( *args )
puts "Foo.show_name says that @name is '#@name' for #{self.inspect}"
end
def initialize( name )
@name = name
end
end

f = Foo.new( "Jim" )

my_lambda = lambda{
puts "my_lambda says that @name is '#@name' for #{self.inspect}"
}
p my_lambda
f.instance_eval( &my_lambda )

my_proc = Proc.new{
puts "my_proc says that @name is '#@name' for #{self.inspect}"
}
p my_proc
f.instance_eval( &my_proc )

meth_lambda = Foo.method( :show_name ).to_proc
p meth_lambda
f.instance_eval( &meth_lambda )

just because you are call meth_lambda in instance_eval doesn't mean @name will
be defined. imagine that you'd written this instead

f.instance_eval{ Foo::show_name }

of course this is exactly what's happening - you've used the class Foo to look
up a class method that prints an uninitialized class variable. the fact that
this whole business gets wrapped in another proc
(Foo.method:)show_name).to_proc) still doesn't initialize it nor bind the
instance's @name to the class @name somehow. probably doing something like

f.instance_eval do
f.class.instance_eval{ @name = 'Jim' }
meth_lambda.call
end

might though.

another approach would be (i think) to 'install' the class method as an
instance one using define_method or something.

cheers.

-a
--
===============================================================================
| email :: ara [dot] t [dot] howard [at] noaa [dot] gov
| phone :: 303.497.6469
| My religion is very simple. My religion is kindness.
| --Tenzin Gyatso
===============================================================================
 
J

Jacob Fugal

On Wed, 20 Jul 2005, Gavin Kistner wrote:
=20
[Slim:~/Desktop] gavinkis% cat instance_eval_proc.rb
class Foo
def self.show_name( *args )
puts "Foo.show_name says that @name is '#@name' for #{self.inspec= t}"
end
def initialize( name )
@name =3D name
end
end

f =3D Foo.new( "Jim" )

my_lambda =3D lambda{
puts "my_lambda says that @name is '#@name' for #{self.inspect}"
}
p my_lambda
f.instance_eval( &my_lambda )

my_proc =3D Proc.new{
puts "my_proc says that @name is '#@name' for #{self.inspect}"
}
p my_proc
f.instance_eval( &my_proc )

meth_lambda =3D Foo.method( :show_name ).to_proc
p meth_lambda
f.instance_eval( &meth_lambda )
=20
just because you are call meth_lambda in instance_eval doesn't mean
@name will be defined.

I think Gavin's point is that it *does* work for my_lambda and
my_proc. meth_lambda is the only exception. Neither my_lambda nor
my_proc knew about @name either when they were defined. instance_eval
bound self to the caller for the evaluation of the proc. However, the
same binding didn't take place for meth_lambda, and it used Foo as
self instead of f.

My guess as to the reason for this is that instance_eval will _supply_
a binding for self where there was none, but won't/can't _override_ an
existing binding for self. Since meth_lambda already has Foo bound to
self, f isn't bound and you get the observed behavior.

That said, if we have to ability to supply a binding to self, it's
hard to believe that we _can't_ override an existing binding. That
would suggest that either:

1) It's a design decision to not override an existing binding for self
2) It's an oversight that can be fixed

Anyone more familiar with the internals want to judge between those
options (or propose a third)?

Jacob Fugal
 
A

Ara.T.Howard

[Slim:~/Desktop] gavinkis% cat instance_eval_proc.rb
class Foo
def self.show_name( *args )
puts "Foo.show_name says that @name is '#@name' for #{self.inspect}"
end
def initialize( name )
@name = name
end
end

f = Foo.new( "Jim" )

my_lambda = lambda{
puts "my_lambda says that @name is '#@name' for #{self.inspect}"
}
p my_lambda
f.instance_eval( &my_lambda )

my_proc = Proc.new{
puts "my_proc says that @name is '#@name' for #{self.inspect}"
}
p my_proc
f.instance_eval( &my_proc )

meth_lambda = Foo.method( :show_name ).to_proc
p meth_lambda
f.instance_eval( &meth_lambda )

just because you are call meth_lambda in instance_eval doesn't mean
@name will be defined.

I think Gavin's point is that it *does* work for my_lambda and
my_proc. meth_lambda is the only exception. Neither my_lambda nor
my_proc knew about @name either when they were defined. instance_eval
bound self to the caller for the evaluation of the proc. However, the
same binding didn't take place for meth_lambda, and it used Foo as
self instead of f.

well:

harp:~ > ri Object.method

---------------------------------------------------------- Object#method
obj.method(sym) => method
------------------------------------------------------------------------
Looks up the named method as a receiver in _obj_, returning a
+Method+ object (or raising +NameError+). The +Method+ object acts
as a closure in _obj_'s object instance, so instance variables and
the value of +self+ remain available.
...
...
...

so i don't really see the exception - the behaviour of Foo.method:)show_name)
does exactly as advertised - binding the method to the class Foo along with
that notion of self and instance vars. so the binding of self for the
duration of instance_eval is definitely working - what gavin is attempting to
do is akin to hoping

f.instance_eval do
p ::File::LOCK_EX
end

would somehow print some other class/objects constant named 'LOCK_EX' in that
the caller/scope-resolution for show_name is already set after the call to
Foo::method. in the cases like

f.instance_eval &my_proc

it has not already been set and is then set for the call. eg. Foo::method
binds exactly as f.instance_eval does - only a layer deeper.

anyhow it seems consistent - but perhaps not intuitive.

kind regards.


-a
--
===============================================================================
| email :: ara [dot] t [dot] howard [at] noaa [dot] gov
| phone :: 303.497.6469
| My religion is very simple. My religion is kindness.
| --Tenzin Gyatso
===============================================================================
 
D

Daniel Brockman

Ara.T.Howard said:
the behaviour of Foo.method:)show_name) does exactly as
advertised - binding the method to the class Foo along
with that notion of self and instance vars.

That is not in dispute.
so the binding of self for the duration of instance_eval
is definitely working

Wait, wait, wait... timeout!

Isn't the whole point of =E2=80=98instance_eval=E2=80=99 to change the
binding of =E2=80=98self=E2=80=99?

(I must admit I never fully understood the scoping rules,
especially when various kinds of =E2=80=98eval=E2=80=99 is involved.)
in the cases like

f.instance_eval &my_proc

it has not already been set and is then set for the call.
eg. Foo::method binds exactly as f.instance_eval does -
only a layer deeper.

So, in Ruby, there are two kinds of procs =E2=80=94 ones that are
bound =E2=80=9Cdeeply=E2=80=9D, and ones that are bound =E2=80=9Cshallowl=
y=E2=80=9D? Well,
that's an interesting concept.
anyhow it seems consistent - but perhaps not intuitive.

It seems consistent to me only after you introduced the
concept of =E2=80=9Cdeeply-bound=E2=80=9D vs. =E2=80=9Cshallowly-bound=E2=
=80=9D procs.

--=20
Daniel Brockman <[email protected]>

So really, we all have to ask ourselves:
Am I waiting for RMS to do this? --TTN.
 
J

Jacob Fugal

=20
That is not in dispute.
=20
=20
Wait, wait, wait... timeout!
=20
Isn't the whole point of 'instance_eval' to change the
binding of 'self'?

Indeed. "Fighting ri with ri", we have from ri Object#instance_eval:

Evaluates a string containing Ruby source code, or the given block,
within the context of the receiver (_obj_). In order to set the
context, the variable +self+ is set to _obj_ while the code is
executing, giving the code access to _obj_'s instance variables.

So, according to documentation, instance_eval should set +self+ to
_obj_ while the code is executing. However, this binding doesn't
happen if there's already a binding for self. Question is, should it?
If so, there's a bug. If not, the documentation needs to reflect that.

Jacob Fugal
 
A

Ara.T.Howard

Indeed. "Fighting ri with ri", we have from ri Object#instance_eval:

Evaluates a string containing Ruby source code, or the given block,
within the context of the receiver (_obj_). In order to set the
context, the variable +self+ is set to _obj_ while the code is
executing, giving the code access to _obj_'s instance variables.

So, according to documentation, instance_eval should set +self+ to
_obj_ while the code is executing. However, this binding doesn't
happen if there's already a binding for self. Question is, should it?
If so, there's a bug. If not, the documentation needs to reflect that.

the docs are consistent, let me show it another way:

harp:~ > cat a.rb
class C
#
# this operates similarly to 'method'
#
def C::self_boxed
self.instance_eval{ self }
end
end

a = Array::new
h = Hash::new

a.instance_eval{ p [self, C::self_boxed] }
h.instance_eval{ p [self, C::self_boxed] }


harp:~ > ruby a.rb
[[], C]
[{}, C]

this is similar to the way Class::method works - self has already been
over-ridden. so in a case like

obj = Object::new
class C; end

obj.instance_eval{ C::method('some_method').to_proc.call }

then we have the case where 'self' is first swapped to 'obj' in order to call
a block that then swaps 'self' for 'C'. so no matter how we wrap

C::method('some_method').to_proc.call

we will always find 'self' == C when we call the proc returns off of the
Method object which wraps 'some_method'. that's because it works in a way
roughly similar to 'self_boxed' in that it's already set up a layer of self
swap'ing.

one more example:

harp:~ > cat a.rb
a = 'a'
b = 'b'

la = lambda{ a.instance_eval{ self } }
lb = lambda{ b.instance_eval{ [la(), self] } }

p lb()


harp:~ > ruby a.rb
["a", "b"]

so you can't override self in a block that overrides self and Class#method is
just that kind of method. uggh that's confusing. ;-)

cheers.


-a
--
===============================================================================
| email :: ara [dot] t [dot] howard [at] noaa [dot] gov
| phone :: 303.497.6469
| My religion is very simple. My religion is kindness.
| --Tenzin Gyatso
===============================================================================
 
P

Phrogz

the fact that this whole business gets wrapped in another proc
(Foo.method:)show_name).to_pro­c) still doesn't initialize it nor bind the
instance's @name to the class @name somehow.

Perhaps this is the source of my confusion. Are you saying that:
my_proc = inst.method( :method_name ).to_proc
is equivalent to:
my_proc = Proc.new{ inst.call( :method_name ) }
and not (as I had assumed):
my_proc = inst.method( :method_name ).unbind.turn_into_a_proc

The documentation is somewhat terse on this matter. Method#to_proc
simply says "Returns a Proc object corresponding to this method."
 
P

Phrogz

[a bunch of snipped examples]

Ara, thanks for helping investigate this. Your C::self_boxed method is
being called with an explicit receiver (C), which is, of course, what
I'm trying to avoid/change using instance_eval.

Two examples from languages with first-class functions:
Lua:
C = { name='C' }
function C:show_name( )
print( self.name )
end

b = { name='bob' }
C.show_name( b ) --> "bob"
-- Here, 'b' is being passed as the first implicit
-- 'self' parameter to show_name

JavaScript:
Array.prototype.show_length = function( ){
alert( this.length )
}
var a = [1,2,3];
var foo = "I'm a string with a length property";
a.show_length.call( foo ); //"35"
//a.show_length looks up the Function instance
//which has a "call" method that allows you to change
//the scope of 'this'

I know that Ruby doesn't currently have first-class functions, but I'm
trying to determine if there's a way to do the equivalent of the above.
Is there any way to invoke a method defined on one class within the
scope of an instance of any other class?
 
P

Pit Capitain

Phrogz said:
I know that Ruby doesn't currently have first-class functions, but I'm
trying to determine if there's a way to do the equivalent of the above.

As has been shown in the original post, Ruby's lambdas are what you call
first-class functions. When called via instance_eval, they are executed
within a new self and have access to self's instance variables and methods.
Is there any way to invoke a method defined on one class within the
scope of an instance of any other class?

It seems that a method, even if unbound or converted to a proc, always
keeps a connection to its class. I think the only way I've seen yet to
achieve what you want was using the evil library.

Regards,
Pit
 
P

Phrogz

Pit said:
As has been shown in the original post, Ruby's lambdas are what you call
first-class functions. When called via instance_eval, they are executed
within a new self and have access to self's instance variables and methods.

Ack, very true. I withdraw that statement. It's just that (as you say)
the current implementation of methods does not have them being
first-class functions. (From my tests, it looks like blocks, however,
are.)
 

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,769
Messages
2,569,582
Members
45,070
Latest member
BiogenixGummies

Latest Threads

Top