self in blocks

  • Thread starter Vasco Andrade e silva
  • Start date
V

Vasco Andrade e silva

Hi

This seems to me somehow inconsistent:

class A; end

# 1)
# output A
A.class_eval { puts self }

# 2)
# output main (in irb)
[1].each { puts self }

The "problem" to me is that in 1) 'self' seems to refer to the context
where yield is called, where in 2) seems to refer to the context in
execution.
What should i expect from 'self'`s behaviour?

Tks
Vasco A. Silva
 
M

MenTaLguY

The "problem" to me is that in 1) 'self' seems to refer to the context
where yield is called, where in 2) seems to refer to the context in
execution.
What should i expect from 'self'`s behaviour?

self will normally refer to self in the lexical scope of the block (#2). class_eval, module_eval, instance_eval, and instance_exec are special exceptions to this rule.

-mental
 
D

dblack

Hi --

Hi

This seems to me somehow inconsistent:

class A; end

# 1)
# output A
A.class_eval { puts self }

# 2)
# output main (in irb)
[1].each { puts self }

The "problem" to me is that in 1) 'self' seems to refer to the context
where yield is called, where in 2) seems to refer to the context in
execution.
What should i expect from 'self'`s behaviour?

self doesn't really behave; it just plays the role it's assigned to
play in different circumstances. class_eval sets self to the
receiver (it's specially designed for that purpose -- basically a
programmatic rather than declarative way to enter a class definition
block). In general, though, self doesn't change when you enter a
code block. That's why self is still main inside your second block.


David

--
Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
(See what readers are saying! http://www.rubypal.com/r4rrevs.pdf)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)
 
G

Gary Wright

# 1)
# output A
A.class_eval { puts self }

One of the purposes of of #class_eval is to explicitly change
the binding of self within the block. By calling #class_eval
you are asking that self be bound to A (for your example) while
the block is executing. This is a behavior of class_eval and
not of blocks in general, which is why in:

[1].each { puts self }

self isn't bound to the array.

Gary Wright
 
V

Vasco Andrade e silva

MenTaLguY said:
self will normally refer to self in the lexical scope of the block (#2).
class_eval, module_eval, instance_eval, and instance_exec are special
exceptions to this rule.

-mental

I confess I don't like this approach :S (rules with exceptions, doesn't
fit right..) however thanks for the explanations.
By the way are class_eval, module_eval, instance_eval and instance_exec
the only exceptions?

Vasco A. Silva
 
D

dblack

Hi --

I confess I don't like this approach :S (rules with exceptions, doesn't
fit right..) however thanks for the explanations.
By the way are class_eval, module_eval, instance_eval and instance_exec
the only exceptions?

I think so. (I can't remember what instance_exec does/will do, but
that's neither here nor there.) It's not really rule plus exception;
it's more a kind of hand-crafted quality to the language that provides
a nice assortment of the facilities that people actually need, with
attention paid to the naming. It's another example of why I have
always said that Ruby represents "the triumph of balance over
symmetry" :)


David

--
Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
(See what readers are saying! http://www.rubypal.com/r4rrevs.pdf)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)
 
M

MenTaLguY

By the way are class_eval, module_eval, instance_eval and instance_exec
the only exceptions?

In Ruby's core library, yes. It's possible to implement new methods with similar behavior (which is occasionally useful), but that isn't the norm.

-mental
 
P

Phrogz

In Ruby's core library, yes. It's possible to implement new methods with similar behavior
(which is occasionally useful), but that isn't the norm.

For example:

class Person
attr_accessor :name, :age
def initialize( name )
@name = name
@age = 0
end
def grow_older
@age += 1
end
end

def create( name, &block )
person = Person.new( name )
person.instance_eval( &block )
p person
end

create( :gavin ){
@age = 32
grow_older
}
#=> #<Person:0x28347c4 @age=33, @name=:gavin>



Using instance eval makes some DSLs (domain specific languages) easier
to create, rather than having to use the object yielded to the block
in every call. Compare the usage of 'create' above to this usage:

def create( name )
person = Person.new( name )
yield person
p person
end

create( :gavin ){ |person|
person.age = 32
person.grow_older
}
#=> #<Person:0x28347c4 @age=33, @name=:gavin>
 
P

Phrogz

Using instance eval makes some DSLs (domain specific languages) easier
to create, rather than having to use the object yielded to the block
in every call.

Here's a real-world example of a DSL I created at work:

bp( 9 ){
lopa( '02' ){
sb "New Project Workflow.ppt"
cr 1331

sb "Add to Online Repository.ppt"
crs 2216, 3560
keywords "Workspace", "Phase 2"
}
}

For both blocks, the function creates an instance of a particular
class and then uses instance_eval to ensure that the methods
referenced within that block are called on the appropriate object. If
I had not mucked about with the self inside the blocks, I would have
had to write something like:

bp( 9 ){ |blockpoint|
blockpoint.lopa( '02' ){ |section|
section.sb "New Project Workflow.ppt"
section.cr 1331

section.sb "Add to Online Repository.ppt"
section.crs 2216, 3560
section.keywords "Workspace", "Phase 2"
}
}
 
V

Vasco Andrade e silva

I agree with you (Gavin Kistner). The question is, what could you loose
if self was always set as it is with class_eval, instance_eval, etc.?
You could still do:
def create( name )
person = Person.new( name )
yield person
p person
end

create( :gavin ){ |person|
person.age = 32
person.grow_older
}
as all the other things.

For me the question is: What's the purpose of not always setting self to
yield context?

Vasco A. Silva
 
D

dblack

Hi --

I agree with you (Gavin Kistner). The question is, what could you loose
if self was always set as it is with class_eval, instance_eval, etc.?
You could still do:
as all the other things.

For me the question is: What's the purpose of not always setting self to
yield context?

Why constrain it to be one way or the other, instead of being able to
do either? Besides, I want to be able to do this:

@x = 1
some_method {|n| puts n + @x }

and not have it be some other object's @x.


David

--
Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
(See what readers are saying! http://www.rubypal.com/r4rrevs.pdf)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)
 
V

Vasco Andrade e silva

Why constrain it to be one way or the other, instead of being able to
do either? Besides, I want to be able to do this:

@x = 1
some_method {|n| puts n + @x }

and not have it be some other object's @x.


David

Excellent!! Your post said everything!
I couldn't be more agree (now)! (I can see the light now ;) )
Thanks a lot!

Vasco A. Silva
 
R

Rick DeNatale

In Ruby's core library, yes. It's possible to implement new methods with similar behavior (which is occasionally useful), but that isn't the norm.

Don't forget define_method .
 
V

Vasco Andrade e Silva

MenTaLguY said:
In Ruby's core library, yes. It's possible to implement new methods
with similar behavior (which is occasionally useful), but that isn't the
norm.

-mental

How can i do that?

I was thinking.. If I want to "bind" self to yield context, with 0
params, i can use a solution like this one:
def create( name, &block )
person = Person.new( name )
person.instance_eval( &block )
p person
end

person.instance_eval( &block ) # some kind of yield(self) inside
instance_eval

but what if i want yield with params, and still have self like
instance_eval?

Example:
class A
def some_method(&block)
yield:)arg1, :arg2)
end
end
a = A.new
# wanted: self "equals" to a
a.some_method { |arg1, arg2| p self }

Vasco A. Silva
 
R

Robert Dober

How can i do that?

I was thinking.. If I want to "bind" self to yield context, with 0
params, i can use a solution like this one:


person.instance_eval( &block ) # some kind of yield(self) inside
instance_eval

but what if i want yield with params, and still have self like
instance_eval?

Example:
class A
def some_method(&block)
yield:)arg1, :arg2)
end
end
a = A.new
# wanted: self "equals" to a
a.some_method { |arg1, arg2| p self }

Vasco A. Silva

Now this is really *ugly*, but maybe you can make it less ugly?

class A
def a &blk
blk.call self, 42
end
end

A.new.a { |o,x| puts "#{self}: #{x}" }
A.new.a { |o,x| o.instance_eval{puts "#{self}: #{x}"} }

HTH
Robert
 
D

dblack

Hi --

How can i do that?

I was thinking.. If I want to "bind" self to yield context, with 0
params, i can use a solution like this one:


person.instance_eval( &block ) # some kind of yield(self) inside
instance_eval

but what if i want yield with params, and still have self like
instance_eval?

Example:
class A
def some_method(&block)
yield:)arg1, :arg2)
end
end
a = A.new
# wanted: self "equals" to a
a.some_method { |arg1, arg2| p self }

That's actually what instance_exec does:

# Requires Ruby 1.9
class A
def x(&block)
instance_exec(1,2,&block)
end
end

A.new.x {|a,b| p a, b, self }

# Output
1
2
#<A:0xb7f00700>

I still don't know what the rationale is for the name, or how one is
supposed to know that instance_exec does it this way and instance_eval
does it the other way (except that I've been using instance_eval for
longer, but that won't help people who are just starting to learn
Ruby).


David

--
Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
(See what readers are saying! http://www.rubypal.com/r4rrevs.pdf)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)
 
R

Rick DeNatale

That's actually what instance_exec does:

# Requires Ruby 1.9
class A
def x(&block)
instance_exec(1,2,&block)
end
end

Or in Ruby 1.8.x you can use Mauricio's version:
http://eigenclass.org/hiki/bounded+space+instance_exec

I still don't know what the rationale is for the name, or how one is
supposed to know that instance_exec does it this way and instance_eval
does it the other way (except that I've been using instance_eval for
longer, but that won't help people who are just starting to learn
Ruby).

I'm not exactly sure where the name came from. Matz mentioned it in
his RubyConf 2005 Keynote, but it seems that he got the idea, (and the
name?) from someone posting on ruby-talk It only shows up as a
one-line item on one of his last charts.
http://glu.ttono.us/articles/2005/10/16/matzs-keynotes

_why gave an implementation here:
http://redhanded.hobix.com/inspect/aBlockCostume.html

This seems a little more naive since it always uses the same name for
the method :_cloaker, and it's not thread-safe.

ActiveSupport in Rails defines Object#instance_exec, but I'm not sure
if this was the chicken or the egg. This implementation uses a similar
technique of defining a method with the proc as the body, rails uses a
timestamp to make the method name 'unique.'


Mauricio's first implementation was a bit more sophisticated in that
it was thread-safe, but it didn't work with immediate value objects
like FixNums.

Mauricio came up with a second version which worked with these, this
version had a memory leak, leading to the latest version.

So that's the history of instance_exec as far as I've been able to
quickly ascertain.

Personally I'd prefer it if instance_exec were used to do this so that

instance_exec(1, 2) {|a, b| ....}

would pass 1 and 2 as the arguments to the block. Perhaps it was felt
that this would lead to confusion with the current definition which
either takes arguments (a string and optional file name and line
number) OR a block, but not both.
 
R

Robert Dober

Don't forget define_method .

You are a genius Rick!!!

So eventually I found what OP needs ;)

class A
def a &blk
i = 1
m = methods
i += 1 while methods.include? "x#{i}"
self.class.send :define_method, "x#{i}", &blk
send "x#{i}", 42
self.class.send :remove_method, "x#{i}"
end
end


a = A.new
a.a {|p| puts "#{self}: #{p}"}
puts a.methods

Probably worth looking into Mauricios Eigenklass. He probably did it
much better maybe caching the method (no idea how to avoid potential
conflicts) ...

Cheers
Robert
 

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

No members online now.

Forum statistics

Threads
473,772
Messages
2,569,593
Members
45,111
Latest member
KetoBurn
Top