Self in blocks


#1

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


#2

On Fri, 11 May 2007 06:37:07 +0900, Vasco Andrade e silva
removed_email_address@domain.invalid wrote:

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


#3

Hi –

On Fri, 11 May 2007, Vasco Andrade e silva wrote:

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


#4

On May 10, 2007, at 5:37 PM, Vasco Andrade e silva wrote:

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


#5

Hi –

On Fri, 11 May 2007, Vasco Andrade e silva wrote:

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” :slight_smile:

David


#6

MenTaLguY wrote:

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


#7

On May 10, 4:21 pm, MenTaLguY removed_email_address@domain.invalid wrote:

On Fri, 11 May 2007 07:06:29 +0900, Vasco Andrade e silva removed_email_address@domain.invalid wrote:

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.

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>


#8

On May 10, 4:50 pm, Phrogz removed_email_address@domain.invalid wrote:

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"

}
}


#9

On Fri, 11 May 2007 07:06:29 +0900, Vasco Andrade e silva
removed_email_address@domain.invalid wrote:

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


#10

I agree with you (Gavin K.). 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


#11

Hi –

On Fri, 11 May 2007, Vasco Andrade e silva wrote:

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?

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


#12

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 :wink: )
Thanks a lot!

Vasco A. Silva


#13

On 5/10/07, MenTaLguY removed_email_address@domain.invalid wrote:

On Fri, 11 May 2007 07:06:29 +0900, Vasco Andrade e silva removed_email_address@domain.invalid wrote:

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.

Don’t forget define_method .


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/


#14

On 5/17/07, Vasco Andrade e Silva removed_email_address@domain.invalid wrote:

params, i can use a solution like this one:
but what if i want yield with params, and still have self like
a.some_method { |arg1, arg2| p self }

Vasco A. Silva


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

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


#15

MenTaLguY wrote:

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


#16

Hi –

On Thu, 17 May 2007, Vasco Andrade e Silva wrote:

params, i can use a solution like this one:
but what if i want yield with params, and still have self like
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


#17

On 5/11/07, Rick DeNatale removed_email_address@domain.invalid wrote:

On 5/10/07, MenTaLguY removed_email_address@domain.invalid wrote:

On Fri, 11 May 2007 07:06:29 +0900, Vasco Andrade e silva removed_email_address@domain.invalid wrote:

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.

Don’t forget define_method .

You are a genius Rick!!!

So eventually I found what OP needs :wink:

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


#18

On 5/17/07, removed_email_address@domain.invalid removed_email_address@domain.invalid wrote:

On Thu, 17 May 2007, Vasco Andrade e Silva wrote:

instance_eval

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

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.


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/