What's the difference between send and instance_eval?

M

michele

What's the difference between send and instance_eval (except the
obvious syntax)?

I've been trying to figure out why Ruby has them both. It seems you can
use them intechangeably, at least in the cases I've found and tested
(so the question is if there is a case where can't change one for the
other).

Sorry if this has been answered already. I really tried to find the
answers in my Ruby and Rails books and on the Internet before posting
the question here.
 
T

Tim Pease

What's the difference between send and instance_eval (except the
obvious syntax)?

I've been trying to figure out why Ruby has them both. It seems you can
use them intechangeably, at least in the cases I've found and tested
(so the question is if there is a case where can't change one for the
other).

Sorry if this has been answered already. I really tried to find the
answers in my Ruby and Rails books and on the Internet before posting
the question here.

send can only be used to execute existing methods on objects.

instance_eval allows you to do much more -- i.e. adding instance
variables, tinkering with private methods, etc

ary = %w( 1 2 3 4 )

ary.send :length #=> 4

ary.instance_eval do
@length = self.length
end

ary.instance_variable_get :mad:length #=> 4


It's a dumb example, but it should convery the differences.

Blessings,
TwP
 
M

michele

So there is no need for "send"?

Thanks


instance_eval allows you to do much more -- i.e. adding instance
variables, tinkering with private methods, etc

ary = %w( 1 2 3 4 )

ary.send :length #=> 4

ary.instance_eval do
@length = self.length
end

ary.instance_variable_get :mad:length #=> 4

It's a dumb example, but it should convery the differences.

Blessings,
TwP
 
J

Joel VanderWerf

michele said:
So there is no need for "send"?

One use of #send:

a=[]
@x=3
a.send :<<, @x
p a

@x=4
a.instance_eval { self << @x }
p a

__END__

Output:

[3]
[3, nil]

This is because @x is in a different scope inside the instance_eval block.

Another case, showing the real point of #send:

msg = :reverse
a = [1,2,3]
b = a.send msg
p b

__END__

Output:

[3, 2, 1]

There's no easy equivalent with #instance_eval and blocks (you could
mess with strings, though).
 
T

Tim Pease

So there is no need for "send"?

Thanks

Actually, I use send quite often -- more so than instance_eval. When I
write unit tests, I use send all the time to call the private methods
of the objects I'm testing.

I use instance_eval when I'm doing things with domain specific languages (DSLs).

Both are equally useful -- it's just a matter of learning when to use which one.

Oh, and no need to apologize for asking questions. The people here are
quite friendly.

Blessings,
TwP

By the way, here are two good articles about DSLs ... one is theory,
the other is practice.

http://www.oreillynet.com/ruby/blog/2005/12/what_is_a_dsl.html
http://www.artima.com/rubycs/articles/ruby_as_dsl.html
 
T

Tomasz Wegrzanowski

So there is no need for "send"?

But there obviously is - method in send doesn't have to be a constant.

# A delegator
def method_missing(m, args, &blk)
@obj.send(m, args, &blk)
end

Now try that with instance_eval ;-)
 
W

Wilson Bilkovich

But there obviously is - method in send doesn't have to be a constant.

# A delegator
def method_missing(m, args, &blk)
@obj.send(m, args, &blk)
end

Now try that with instance_eval ;-)

def method_missing(m, args, &block)
@obj.instance_eval { self.send(m, args, &block) }
end

Heh.
 
M

michele

This works:

msg = :reverse
a = [1,2,3]
a.instance_eval(msg.to_s)


michele said:
So there is no need for "send"?One use of #send:

a=[]
@x=3
a.send :<<, @x
p a

@x=4
a.instance_eval{ self << @x }
p a

__END__

Output:

[3]
[3, nil]

This is because @x is in a different scope inside theinstance_evalblock.

Another case, showing the real point of #send:

msg = :reverse
a = [1,2,3]
b = a.send msg
p b

__END__

Output:

[3, 2, 1]

There's no easy equivalent with #instance_evaland blocks (you could
mess with strings, though).
 
J

Joel VanderWerf

Putting the comments in order...
Joel said:
Another case, showing the real point of #send:

msg = :reverse
a = [1,2,3]
b = a.send msg
p b

__END__

Output:

[3, 2, 1]

There's no easy equivalent with #instance_evaland blocks (you could
mess with strings, though).

This works:

msg = :reverse
a = [1,2,3]
a.instance_eval(msg.to_s)


That's true, but only because I chose a bad example. Here's a better one:

h = {1=>2}
msg = [:concat, [4, 5, 6, IO, String, Kernel, h]]
a = [1,2,3]
p a.send(*msg) # ==> [1, 2, 3, 4, 5, 6, IO, String, Kernel, {1=>2}]
p a.instance_eval(msg.to_s) # ==> NameError

To make the instance_eval work here, you would have to find a way to
turn the argument array into a string that, when eval-ed, is that same
array. (It's possible, but painful, and you lose the identity of the
hash h.)
 
R

Rick DeNatale

That's true, but only because I chose a bad example. Here's a better one:

h = {1=>2}
msg = [:concat, [4, 5, 6, IO, String, Kernel, h]]
a = [1,2,3]
p a.send(*msg) # ==> [1, 2, 3, 4, 5, 6, IO, String, Kernel, {1=>2}]
p a.instance_eval(msg.to_s) # ==> NameError

To make the instance_eval work here, you would have to find a way to
turn the argument array into a string that, when eval-ed, is that same
array. (It's possible, but painful, and you lose the identity of the
hash h.)

except that instance_eval can take a block in lieu of a string:

a.instance_eval {concat [1,2,3,4,5,6,IO,String,Kernel, h]}
=> [1, 2, 3, 1, 2, 3, 4, 5, 6, IO, String, Kernel, {1=>2}]

Not that I think that send should be eliminated, both methods are
useful. Horses for courses.
 
J

Joel VanderWerf

Rick said:
That's true, but only because I chose a bad example. Here's a better one:

h = {1=>2}
msg = [:concat, [4, 5, 6, IO, String, Kernel, h]]
a = [1,2,3]
p a.send(*msg) # ==> [1, 2, 3, 4, 5, 6, IO, String, Kernel, {1=>2}]
p a.instance_eval(msg.to_s) # ==> NameError

To make the instance_eval work here, you would have to find a way to
turn the argument array into a string that, when eval-ed, is that same
array. (It's possible, but painful, and you lose the identity of the
hash h.)

except that instance_eval can take a block in lieu of a string:

a.instance_eval {concat [1,2,3,4,5,6,IO,String,Kernel, h]}
=> [1, 2, 3, 1, 2, 3, 4, 5, 6, IO, String, Kernel, {1=>2}]

Not that I think that send should be eliminated, both methods are
useful. Horses for courses.

And I'm starting to beat a dead horse here, but using a block leads you
to scoping issues. (What if the hash were @h or a method call instead of
h? You can always use an temporary local var to avoid the issue.)

You're quite right: each method has its place.
 
M

michele

I may not understand it well, but I don't like it. The two methods are
so similar that the only way to figure out the difference is to find
cases that will make it hard or impossible to use one of them. It
should be easy to choose which method to use, not hard to figure out
which one not to use. They are also conceptually different. An object
can either call a method (most common lingo among Java, C, etc) or you
can send it a message (Smalltalk), but what does "eval" mean? Ruby uses
all three (call, send and eval) which may be good (I don't know why,
but maybe if you like to study computer languages), but if I don't use
these methods every day, I will have to think hard every time I need
one of them.


Rick said:
That's true, but only because I chose a bad example. Here's a better one:
h = {1=>2}
msg = [:concat, [4, 5, 6, IO, String, Kernel, h]]
a = [1,2,3]
p a.send(*msg) # ==> [1, 2, 3, 4, 5, 6, IO, String, Kernel, {1=>2}]
p a.instance_eval(msg.to_s) # ==> NameError
To make the instance_eval work here, you would have to find a way to
turn the argument array into a string that, when eval-ed, is that same
array. (It's possible, but painful, and you lose the identity of the
hash h.)
except that instance_eval can take a block in lieu of a string:
a.instance_eval {concat [1,2,3,4,5,6,IO,String,Kernel, h]}
=> [1, 2, 3, 1, 2, 3, 4, 5, 6, IO, String, Kernel, {1=>2}]
Not that I think that send should be eliminated, both methods are
useful. Horses for courses.And I'm starting to beat a dead horse here, but using a block leads you
to scoping issues. (What if the hash were @h or a method call instead of
h? You can always use an temporary local var to avoid the issue.)

You're quite right: each method has its place.
 

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,744
Messages
2,569,483
Members
44,902
Latest member
Elena68X5

Latest Threads

Top