简体   繁体   中英

Methods in Ruby: objects or not?

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?

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 :
替代文字

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.

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
=> false

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.

Objects and Methods are not the same even if the return value for the methods is an object and not nil. Objects live on the heap unless in a method, lambda, or proc scope and the method itself lives on the stack and has an address assign after interpretation while static and class objects are allocated on the heap. Ruby still uses C to interpret it and pass it to the VALUE struct.

Because parenthesis are optional in ruby, method objects are generally "hidden" in the sense you need to explicitly fetch the method object via the method method. However if you make the effort to capture a method object it becomes quite clear that it acts like an object. Since Ruby >= 2.1 this is easier to take advantage of than ever.

For example, you can get your methods to behave more like they do in Javascript (where no parens is the method object and parens are used to call the method) like so:

foo = method def foo
  def a(num)
    3 * num.to_i
  end

  n = yield if block_given?
  a(n || 3)
rescue
  "oops!"
end

def foo.bar(num)
  a(num)
end

foo.class #=> Method
foo() #=> 9
foo.call #=> 9
foo.call{2} #=> 6
foo(){2} #=> 6
foo.call{ raise "blam!" } #=> "oops!"
foo.bar(5) #=> 15

See this gist for a version with these example written as tests.

JRL's answer quotes Matz book saying that methods are not objects like strings etc. are, but method objects are real, and other than the parens/no-parens thing they act pretty much like any other ruby object. It's a duck-typed language , so I'd say that qualifies methods as objects in my book.

In Ruby, methods are not objects. This is confusing because there is a Method class and you can get instances of Method. These instances are just proxies for the method itself. These instances provide some useful functionality. They have some internal magic that hooks them up to the actual method (so you can do things like Method#call ) but you can't actually access that stuff (AFAIK).

1.method(:to_s).object_id == 1.method(:to_s).object_id #=> false

This means either that 1 has two #to_s methods (which it does not) or that what is returned by the method #method is not actually the method itself but some proxy for the method. If methods were actually objects then you would have situations where you were able to get the same instance twice. If methods were objects then you would be able to do things like set an instance variable on them and them, later, get the value of that instance variable after a second retrieval of the method object. You can't do that. So, while it may generally make no difference, there are situations where I find I am unable to do things that I would like to.

1.method(:to_s).instance_variable_set(:@foo, 'foo') #=> "foo" 
1.method(:to_s).instance_variable_get(:@foo)        #=> nil 
# And just in case you question it...
1.object_id == 1.object_id                          #=> true 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM