Method behaves differently when called using #send

(Ron M) #1

I'm having difficulty using #send to call methods in
some of my classes. In the example shown below,
I can call the method "y" directly with no problems,
but when I try to call it using send I get an
ArgumentError.

>> es.y
=> 30.468257171342
>> es.send(:y)
ArgumentError: wrong number of arguments (0 for 1)
         from (irb):7:in `y'
         from (irb):7
>>

Other methods seem to work just fine using send.

>> es.x
=> -84.2674903100158
>> es.send(:x)
=> -84.2674903100158

This class happens to be an ActiveRecord class
and x and y came from columns in the database,
so unfortunately I don't really know what source
code I could post here to help track this down.

Can anyone think of what I could look for so I
can call y using send or where I should look for
further information to understand why it's not working?

    Thanks,
    Ron

(David A. Black) #2

Hi --

···

On Fri, 26 Aug 2005, Ron M wrote:

I'm having difficulty using #send to call methods in
some of my classes. In the example shown below,
I can call the method "y" directly with no problems,
but when I try to call it using send I get an
ArgumentError.

es.y

=> 30.468257171342

es.send(:y)

ArgumentError: wrong number of arguments (0 for 1)
       from (irb):7:in `y'
       from (irb):7

I wonder whether it's YAML-related. The YAML library defines
Kernel#y. I'm not sure why you'd get that error message from it
though, unless maybe AR hooks into it in some way.

David

--
David A. Black
dblack@wobblini.net

(Yukihiro Matsumoto) #3

Hi,

···

In message "Re: Method behaves differently when called using #send" on Fri, 26 Aug 2005 18:25:25 +0900, Ron M <rm_rails@cheapcomplexdevices.com> writes:

I'm having difficulty using #send to call methods in
some of my classes. In the example shown below,
I can call the method "y" directly with no problems,
but when I try to call it using send I get an
ArgumentError.

>> es.y
=> 30.468257171342
>> es.send(:y)
ArgumentError: wrong number of arguments (0 for 1)
        from (irb):7:in `y'
        from (irb):7
>>

It shouldn't. I'm afraid it's a bug, but I have too few information
to identify or fix the bug.

              matz.

(Peter Vanbroekhoven) #4

Maybe it has something to do with method_missing. I suspect object es has a private method #y which requires one argument. This is the one es.send(:y) calls (as it can call private methods). However when you call the method as just es.y, it is not found and method_missing is called which happens to handle y as a method with no arguments.

Peter

···

On Fri, 26 Aug 2005, Ron M wrote:

Can anyone think of what I could look for so I
can call y using send or where I should look for
further information to understand why it's not working?

(Ron M) #5

David A. Black wrote:

es.y

=> 30.468257171342

es.send(:y)

ArgumentError: wrong number of arguments (0 for 1)
       from (irb):7:in `y'
       from (irb):7

I wonder whether it's YAML-related. The YAML library defines
Kernel#y. I'm not sure why you'd get that error message from it
though, unless maybe AR hooks into it in some way.

I guess one part of my question was more along the lines of

"is it a bug or a feature I don't understand, that
  calling methods through "#send(:name)" vs ".name"
  seem to find different methods"

Regarding the specifics of what weird stuff AR is doing,
would this be a good place to ask, or would the rails
mailing list be better?

   Thanks,
   Ron

(Ron M) #6

calamitas@advalvas.be wrote:

Can anyone think of what I could look for so I
can call y using send or where I should look for
further information to understand why it's not working?

Maybe it has something to do with method_missing. I suspect object es has a private method #y which requires one argument. This is the one es.send(:y) calls (as it can call private methods). However when you call the method as just es.y, it is not found and method_missing is called which happens to handle y as a method with no arguments.

Ah yes!!! That seems to be the case.

It also seems to be a reasonable workaround for me.

>> es.send(:method_missing,:y)
=> 30.468257171342
>>

Thanks!

···

On Fri, 26 Aug 2005, Ron M wrote:

(Yukihiro Matsumoto) #7

Hi,

···

In message "Re: Method behaves differently when called using #send" on Sat, 27 Aug 2005 02:29:10 +0900, calamitas@advalvas.be writes:

Maybe it has something to do with method_missing. I suspect object es has
a private method #y which requires one argument. This is the one
es.send(:y) calls (as it can call private methods). However when you call
the method as just es.y, it is not found and method_missing is called
which happens to handle y as a method with no arguments.

Although this explains the situation, I admit this is confusing. I
decided to make Kernel#send to call public methods only (in 1.9 for
the sake of compatibility). If you want to invoke private methods,
use a new method, #fcall.

              matz.

(David A. Black) #8

Hi --

David A. Black wrote:

es.y

=> 30.468257171342

es.send(:y)

ArgumentError: wrong number of arguments (0 for 1)
       from (irb):7:in `y'
       from (irb):7

I wonder whether it's YAML-related. The YAML library defines
Kernel#y. I'm not sure why you'd get that error message from it
though, unless maybe AR hooks into it in some way.

I guess one part of my question was more along the lines of

"is it a bug or a feature I don't understand, that
calling methods through "#send(:name)" vs ".name"
seem to find different methods"

Certainly this:

   ruby -e 'def x; end; x; send(:x)'

should never give you an ArgumentError, for any valid method name in
place of x. If it does, there's a bug somewhere.

If, however, something in your code, or code that yours loads, has
changed things, such that you get that error, then it's not a bug.

Regarding the specifics of what weird stuff AR is doing,
would this be a good place to ask, or would the rails
mailing list be better?

Maybe -- you could try both.

David

···

On Sat, 27 Aug 2005, Ron M wrote:

--
David A. Black
dblack@wobblini.net

(Berger, Daniel) #9

Ron M wrote:

David A. Black wrote:

es.y

=> 30.468257171342

es.send(:y)

ArgumentError: wrong number of arguments (0 for 1)
       from (irb):7:in `y'
       from (irb):7

I wonder whether it's YAML-related. The YAML library defines
Kernel#y. I'm not sure why you'd get that error message from it
though, unless maybe AR hooks into it in some way.

I wouldn't think so. At least, my own experiments didn't reveal this to be an issue, but you never know.

Just for kicks, does it make any difference if you use es.__send__(:y)?

Otherwise, I recommend firing up the debugger and stepping into the method to see where it takes you.

Regards,

Dan

(Simon Kröger) #10

Maybe it has something to do with method_missing. I suspect object es has a private method #y which requires one argument. This is the one es.send(:y) calls (as it can call private methods). However when you call the method as just es.y, it is not found and method_missing is called which happens to handle y as a method with no arguments.

Wow Peter, nice one to figure that out!

···

Ah yes!!! That seems to be the case.

It also seems to be a reasonable workaround for me.

>> es.send(:method_missing,:y)
=> 30.468257171342
>>

-------------------------------------------
class A
   def b
     puts "this is b!"
   end
   private :b
  
   def method_missing name
     puts "this is method missing: #{name}"
   end
end

a = A.new

a.b
a.send(:b)
eval "a.#{:b.to_s}"
-------------------------------------------
output:

this is method missing: b
this is b!
this is method missing: b

-------------------------------------------

I would resort to eval in this case, rather than relying
on implementation details. (but its slower, for sure)

cheers

Simon

(David A. Black) #11

Hi --

···

On Sat, 27 Aug 2005, Yukihiro Matsumoto wrote:

Hi,

In message "Re: Method behaves differently when called using #send" > on Sat, 27 Aug 2005 02:29:10 +0900, calamitas@advalvas.be writes:

>Maybe it has something to do with method_missing. I suspect object es has
>a private method #y which requires one argument. This is the one
>es.send(:y) calls (as it can call private methods). However when you call
>the method as just es.y, it is not found and method_missing is called
>which happens to handle y as a method with no arguments.

Although this explains the situation, I admit this is confusing. I
decided to make Kernel#send to call public methods only (in 1.9 for
the sake of compatibility). If you want to invoke private methods,
use a new method, #fcall.

What's the 'f' in 'fcall'? (I'm just wondering why it's not 'mcall'.)

David

--
David A. Black
dblack@wobblini.net

(David A. Black) #12

Hi --

···

On Sat, 27 Aug 2005, Yukihiro Matsumoto wrote:

Hi,

In message "Re: Method behaves differently when called using #send" > on Sat, 27 Aug 2005 02:29:10 +0900, calamitas@advalvas.be writes:

>Maybe it has something to do with method_missing. I suspect object es has
>a private method #y which requires one argument. This is the one
>es.send(:y) calls (as it can call private methods). However when you call
>the method as just es.y, it is not found and method_missing is called
>which happens to handle y as a method with no arguments.

Although this explains the situation, I admit this is confusing. I
decided to make Kernel#send to call public methods only (in 1.9 for
the sake of compatibility). If you want to invoke private methods,
use a new method, #fcall.

Here's another idea:

Kernel#send
Kernel#send! # dangerous version, includes private methods

David

--
David A. Black
dblack@wobblini.net

(Peter Vanbroekhoven) #13

Something has been bothering me about this still, and now I know what. Kernel#send used to call the given method as if it was being called without receiver (thus being able to call private methods). Changing Kernel#send does not change how calling a method without receiver works, so the confusion persists there. For example:

   class A
     def m1(x)
       "m1/1"
     end
     private :m1
     def method_missing(m, *args)
       return "m1/2" if m == :m1
       super
     end
     def use_m1
       p m1(5) # prints "m1/1"
       p self.m1(5) # prints "m1/2"
       t = self
       p t.m1(5) # prints "m1/2"
     end
   end

   A.new.use_m1

I only see two ways of avoiding this confusion. The first is not to call method_missing for private/protected methods. In a way that makes sense because if a method is private, it is not missing, just inaccessible. The consequence would be that method calls with an without receiver do exactly the same, or the one with receiver gives an error.

The second possibility would be to give methods implemented through method_missing precedence over private methods. Thus without receiver, first a public method is sought for, if not found method_missing is called, if that fails, the private method is called if present. So essentially it tries the call with receiver, then it tries without.

I think it's still OK to have two versions of Kernel#send, one that does the call as if with receiver, one as if without receiver (which can call private methods) -- however you name them. But the confusion is not because of Kernel#send; it's because of the semantics of method calls with receiver vs without receiver, and changing Kernel#send does not solve that. Well, IMHO at least.

Or am I missing the point completely?

Peter

PS: fcall is used in Ruby's internals to refer to a call without receiver. This looks like a call to a procedural function, hence fcall.

···

On Sat, 27 Aug 2005, Yukihiro Matsumoto wrote:

Although this explains the situation, I admit this is confusing. I
decided to make Kernel#send to call public methods only (in 1.9 for
the sake of compatibility). If you want to invoke private methods,
use a new method, #fcall.

(Yukihiro Matsumoto) #14

Hi,

···

In message "Re: Method behaves differently when called using #send" on Sat, 27 Aug 2005 23:44:52 +0900, "David A. Black" <dblack@wobblini.net> writes:

Here's another idea:

Kernel#send
Kernel#send! # dangerous version, includes private methods

It's bit different from my sense of dangerousness, but let me
consider.

              matz.

(David A. Black) #15

Hi --

Although this explains the situation, I admit this is confusing. I
decided to make Kernel#send to call public methods only (in 1.9 for
the sake of compatibility). If you want to invoke private methods,
use a new method, #fcall.

Something has been bothering me about this still, and now I know what. Kernel#send used to call the given method as if it was being called without receiver (thus being able to call private methods). Changing Kernel#send does not change how calling a method without receiver works, so the confusion persists there. For example:

class A
   def m1(x)
     "m1/1"
   end
   private :m1
   def method_missing(m, *args)
     return "m1/2" if m == :m1
     super
   end
   def use_m1
     p m1(5) # prints "m1/1"
     p self.m1(5) # prints "m1/2"
     t = self
     p t.m1(5) # prints "m1/2"
   end
end

A.new.use_m1

I only see two ways of avoiding this confusion. The first is not to call method_missing for private/protected methods. In a way that makes sense because if a method is private, it is not missing, just inaccessible. The consequence would be that method calls with an without receiver do exactly the same, or the one with receiver gives an error.

I tend to agree that private methods are not "missing". I think that
this:

irb(main):008:0> class C; def x; 1; end; private :x; end
=> C
irb(main):009:0> class C; def method_missing(m); p m; end; end
=> nil
irb(main):010:0> C.new.x
:x

should give a "private method 'x'" error.

Then again, that's actually a NoMethodError.... Maybe it should be a
UnavailableMethodError or something.

The second possibility would be to give methods implemented through method_missing precedence over private methods. Thus without receiver, first a public method is sought for, if not found method_missing is called, if that fails, the private method is called if present. So essentially it tries the call with receiver, then it tries without.

I'm not sure I follow this. If you call a method without receiver, it
would call it *with* receiver first? Or do you mean "with[out]
receiver" behavior would be redefined?

I think it's still OK to have two versions of Kernel#send, one that does the call as if with receiver, one as if without receiver (which can call private methods) -- however you name them. But the confusion is not because of Kernel#send; it's because of the semantics of method calls with receiver vs without receiver, and changing Kernel#send does not solve that. Well, IMHO at least.

It's also true that "send" implies "receive"... so it doesn't really
mean there isn't a receiver. I think I have to let this percolate a
little before I go on :slight_smile:

Or am I missing the point completely?

Peter

PS: fcall is used in Ruby's internals to refer to a call without receiver. This looks like a call to a procedural function, hence fcall.

It does? :slight_smile: I'm still not thrilled with fcall. It seems strange to
me to introduce the concept of a "function" in Ruby just for this one
purpose.

David

···

On Mon, 29 Aug 2005, Peter Vanbroekhoven wrote:

On Sat, 27 Aug 2005, Yukihiro Matsumoto wrote:

--
David A. Black
dblack@wobblini.net

(James Britt) #16

Yukihiro Matsumoto wrote:

Hi,

>Here's another idea:
>
>Kernel#send
>Kernel#send! # dangerous version, includes private methods

It's bit different from my sense of dangerousness, but let me
consider.

I like it. It strikes me as having better semantic value than fcall.

James

···

In message "Re: Method behaves differently when called using #send" > on Sat, 27 Aug 2005 23:44:52 +0900, "David A. Black" <dblack@wobblini.net> writes:

--

http://www.ruby-doc.org - The Ruby Documentation Site
http://www.rubyxml.com - News, Articles, and Listings for Ruby & XML
http://www.rubystuff.com - The Ruby Store for Ruby Stuff
http://www.jamesbritt.com - Playing with Better Toys

(daz) #17

Yukihiro Matsumoto wrote:

David A. Black writes:

>Here's another idea:
>
>Kernel#send
>Kernel#send! # dangerous version, includes private methods

It's bit different from my sense of dangerousness, but let me
consider.

matz.

[replying to David]

It's easy to understand the meaning of #send! when reading
this thread because it is within context but, as Matz implies,
bang! methods usually provide the destructive counterpart
of a non-bang method (i.e. an object is modified in place
rather than being copied first).
     -- Yes, I know you know that :slight_smile:

There's nothing inherently dangerous about bang methods;
we use them quite safely.

Calling private methods could be dangerous but overloading
the meaning of the "!" suffix needs careful consideration
and I understand Matz' concern.

Having said that, my initial reaction to #send! was positive
and, for insiders like yourself and for me too, it should be
easy to remember -- for teaching and learning, though, it
may create an obstacle.

With 5 votes in support, I felt there was room for some balance.
Any of the +1 voters strong enough to change their mind ?

daz

P.S. A ham sandwich is better than #fcall :smiley: [SCNR]

(Malte Milatz) #18

David A. Black wrote:

irb(main):008:0> class C; def x; 1; end; private :x; end => C
irb(main):009:0> class C; def method_missing(m); p m; end; end => nil
irb(main):010:0> C.new.x
:x
should give a "private method 'x'" error.

Then again, that's actually a NoMethodError.... Maybe it should be a
UnavailableMethodError or something.

In my opinion, an object shouldn't tell others about its internals unless
explicitly asked. So a NoMethodError is OK IMHO. Consequently, I agree
with the current behavior calling method_missing here.

Malte

(Peter Vanbroekhoven) #19

The second possibility would be to give methods implemented through method_missing precedence over private methods. Thus without receiver, first a public method is sought for, if not found method_missing is called, if that fails, the private method is called if present. So essentially it tries the call with receiver, then it tries without.

I'm not sure I follow this. If you call a method without receiver, it
would call it *with* receiver first? Or do you mean "with[out]
receiver" behavior would be redefined?

First off, I have to mention that I meant with[out] _explicit_ receiver, of course. Every method call has a receiver.

The idea was that when a method is called without explicit receiver, it is first tried as if it had self as an explicit receiver, and if that fails, the private method is called, if there is one. This makes sure that if the call with explicit receiver succeeds, the call without explicit receiver succeeds in exactly the same way. If the one with fails, the one without can still succeed if there is a private method. It makes sure the capabilities of method calls without explicit receiver are a superset of the ones with explicit receiver. So yes, the "without receiver" behavior would be redefined, the "with receiver" behavior stays the same. In my first option (no method_missing for private methods), it is the other way around.

I think it's still OK to have two versions of Kernel#send, one that does the call as if with receiver, one as if without receiver (which can call private methods) -- however you name them. But the confusion is not because of Kernel#send; it's because of the semantics of method calls with receiver vs without receiver, and changing Kernel#send does not solve that. Well, IMHO at least.

It's also true that "send" implies "receive"... so it doesn't really
mean there isn't a receiver. I think I have to let this percolate a
little before I go on :slight_smile:

Does it help if you replace "with[out] receiver" by "with[out] explicit receiver"? When using Kernel#send (as it is now), it is as if you call the method without explicit receiver, hence the ability to call private methods. Actually in a way it is logical that Kernel#send can call private methods, because once execution is in send, self has changed and the method can be called without explicit receiver, enabling calls to private methods. Say I'd define my own send like this:

   class Object
     def my_send(m, *args, &blk)
       eval "#{m}(*args, &blk)"
     end
   end

   class A
     def m
       1
     end
     private :m
   end

   A.new.m # => NoMethodError: private method `m' called for #<A:0x402d67b4>

   A.new.my_send(:m) # => 1

Sorry, I have a knack for forgetting to say the obvious things (obvious for me). Well, for all I know, I may have done it again :wink:

PS: fcall is used in Ruby's internals to refer to a call without receiver. This looks like a call to a procedural function, hence fcall.

It does? :slight_smile: I'm still not thrilled with fcall. It seems strange to
me to introduce the concept of a "function" in Ruby just for this one
purpose.

Of course, same here; it was only meant as clarification. I don't like fcall myself, though I'm not sure about send! either. Matz is introducing send! because send as it is now is confusing, it violates POLS (I think that is appropriate now as I suspect Matz was surprised by send's behavior). And violating POLS is not the same as being dangerous. Of course calling private methods could be dangerous. So I think I can understand Matz's hesitation here.

Peter

···

On Mon, 29 Aug 2005, David A. Black wrote:

(James Edward Gray II) #20

I agree. It's been my favorite option from this thread.

James Edward Gray II

···

On Aug 27, 2005, at 10:08 AM, James Britt wrote:

Yukihiro Matsumoto wrote:

Hi,
In message "Re: Method behaves differently when called using #send" >> on Sat, 27 Aug 2005 23:44:52 +0900, "David A. Black" >> <dblack@wobblini.net> writes:
>Here's another idea:
>
>Kernel#send
>Kernel#send! # dangerous version, includes private methods
It's bit different from my sense of dangerousness, but let me
consider.

I like it. It strikes me as having better semantic value than fcall.