views:

201

answers:

4

Inspired by this discussion, after some googling I wasn't able to find an answer to a pretty simple question regarding methods in Ruby: are methods objects or not?

There are different opinions here and there, and I would really like to hear, let's say, an in-depth explanation.

I'm aware of Object#method method, which takes a method name and returns a Method instance, but, on the other hand, there's a similar thing you can do with blocks to make them into Proc instances, and blocks aren't objects, so what makes methods any different?

A: 

They are objects, but they're not particularly useful as such. If you're used to Python's approach which allows you to stuff anything you want into them, you'll be disappointed.

A severely contrived example for those who've never done any Python metaprogramming yet still feel the need to shoot their mouths off:

#This is a Python class definition
class TalentlessBlogger(object):

    #This is a Python method definition
    def post(self):
        pass

    #"post", the method which we just defined, is a true first-class object, and
    #can be treated as such right here, right now, before we even leave the class
    #definition. Try this in Ruby and see how far you get.
    post.__hurr__ = "My opinions matter"

def main():
    #Here we see that the bound method "post" has maintained
    #the value which we assigned to an arbitrary attribute 
    #immediately after it was defined.
    print FatBlogger.post.__hurr__

if __name__ == '__main__':
    main() 
Azeem.Butt
wrong. see JRL's answer.
banister
@NSD: I'm kinda curious, what do you mean when you say "stuff anything you want into them"?
Mladen Jablanović
Right. Learn Python and then complain. There is absolutely no way to poke metadata into a method AS IT IS BEING DEFINED in Ruby. JRL's answer is as irrelevant as your comment.
Azeem.Butt
Apart for being insolent, your example is indeed instructive (however, doesn't really address the main question). But it also IMHO _disproves_ your first premise: Ruby methods aren't objects - otherwise it would be possible to add new methods to them (as you did in Python), at any point of execution.
Mladen Jablanović
"The Ruby Programming Language" book referenced by JRL is definitive, it was co-written by the author of Ruby. The book states methods are not objects, they are *not* objects. Nonetheless you can get an object-wrapper *of* a method, i.e a Method instance. Whether or not you can do this or that in Python is completely irrelevant. The question didn't mention Python.
banister
+2  A: 

In Ruby, methods and blocks are not, in and of themselves, native or first-class objects. However, they can very easily be wrapped in objects, such that it generally makes no difference.

But try out, and keep in mind the result of,

a = Object.method(:new).object_id
b = Object.method(:new).object_id
a == b

In Haskell, all values (including numbers as well as lambdas and functions) are first-class values. In every aspect of the language, they are all treated equivalently. This is not the case in Ruby, but it can be approximated.

Justice
Justice, your argument was the same as mine. In addition, I'm not aware of any other way to return methods, so #method(method_name) seems to be the only way to reference a method at all, aside from calling it by name on it's original object.
Tim Snowhite
The only thing that your object_id proves is that they are not immediates. `a = 1.0.object_id; b = 1.0.object_id; a == b # => false`
Marc-André Lafortune
+11  A: 

Methods are a fundamental part of Ruby's syntax, but they are not values that Ruby programs can operate on. That is, Ruby's methods are not objects in the way that strings, numbers, and arrays are. It is possible, however, to obtain a Method object that represents a given method, and we can invoke methods indirectly through Method objects.

From The Ruby Programming Language:
alt text

JRL
+1  A: 

You can't really tell.

The only way to get access to a method is to send the #method message to some object, which will then return a Method object. But is that Method object the method itself? Or is it a wrapper around the method? Or is it a converted version of the original method?

You can't know: if you want to look at a method, you have to call #method, at which point you definitely will get an object. What it was before you called #method you can't look at, therefore you can't tell.

A couple of datapoints: in Ruby, everything returns a value. What does def return? It always returns nil, not a Method object. And define_method? It returns a Proc, but not a Method (nor an UnboundMethod). [Note: in Rubinius, def returns the compiled bytecode of the method, but still not a Method object.]

If you look at the 4th and 5th paragraphs of Section 6.1 of the Ruby Language Specification (lines 29-34 and 1-5 on pages 5 and 6), you can clearly see that there is a distinction drawn between methods and objects. And if you look at the specification of the builtin classes, you will find that neither Method nor UnboundMethod are in there, nor is Object#method. IOW: you can build a perfectly standards-compliant Ruby interpreter in which methods aren't objects.

Now, blocks OTOH definitely aren't objects. There are many ways to construct Proc objects from blocks, which then have the same behavior as the original block (lambda, proc, Proc.new, the & sigil), but blocks themselves aren't objects.

Think about it this way: you can pass a string to File.new to construct a file object, but that doesn't make a string a file. You can pass a block to Proc.new to construct a proc object, but that doesn't make a block a proc.

Jörg W Mittag